opentwig 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Tufan Tunรง
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,191 @@
1
+ # OpenTwig ๐ŸŒฟ
2
+
3
+ OpenTwig is an open source personal link page generator that creates beautiful, customizable "link in bio" pages. Instead of relying on third-party services, users can define their configuration and instantly create a fully functional static site they own and control.
4
+
5
+ ## โœจ Features
6
+
7
+ - ๐ŸŽจ **Multiple Themes**: Choose from 4 built-in themes (default, dark, minimal, colorful)
8
+ - ๐Ÿ“ฑ **Mobile Responsive**: Optimized for all devices with mobile-first design
9
+ - ๐Ÿš€ **Fast & Lightweight**: Generates static HTML/CSS with minimal dependencies
10
+ - ๐Ÿ”— **Easy Link Management**: Simple JSON configuration for all your links
11
+ - ๐Ÿ–ผ๏ธ **Avatar Support**: Custom profile pictures with automatic processing
12
+ - ๐Ÿ“Š **Open Graph Images**: Auto-generated social media preview images
13
+ - ๐Ÿ“ฑ **QR Code Generation**: Built-in QR codes for easy mobile sharing
14
+ - ๐ŸŽญ **Modal Dialogs**: Support for rich content in footer links
15
+ - ๐Ÿ“ค **Share Functionality**: Native Web Share API integration
16
+ - โšก **CSS Optimization**: Automatic CSS minification and autoprefixing
17
+ - ๐Ÿ› ๏ธ **CLI Interface**: Simple command-line interface with helpful commands
18
+
19
+ ## ๐Ÿš€ Quick Start
20
+
21
+ ### Installation & Usage
22
+
23
+ ```bash
24
+ # Create a new project
25
+ npx opentwig --init
26
+
27
+ # Edit the generated config.json with your information
28
+ # Then generate your page
29
+ npx opentwig
30
+ ```
31
+
32
+ ### Prerequisites
33
+
34
+ - Node.js (v14 or higher)
35
+ - npm or yarn
36
+
37
+ ## ๐Ÿ“– Configuration
38
+
39
+ OpenTwig uses a simple JSON configuration file (`config.json`) to define your page. Here's the complete configuration structure:
40
+
41
+ ```json
42
+ {
43
+ "theme": "default",
44
+ "url": "https://links.yourwebsite.com",
45
+ "title": "Your Name - opentwig ๐ŸŒฟ",
46
+ "name": "Your Name",
47
+ "content": "Hello World! Here is my bio.",
48
+ "minify": true,
49
+ "avatar": {
50
+ "path": "./avatar.png"
51
+ },
52
+ "links": [
53
+ {
54
+ "url": "https://twitter.com",
55
+ "title": "Twitter"
56
+ },
57
+ {
58
+ "url": "https://instagram.com",
59
+ "title": "Instagram"
60
+ }
61
+ ],
62
+ "footerLinks": [
63
+ {
64
+ "title": "Contact",
65
+ "url": "mailto:mail@mail.com"
66
+ },
67
+ {
68
+ "title": "Privacy",
69
+ "content": "Your privacy policy content here..."
70
+ }
71
+ ],
72
+ "share": {
73
+ "title": "Your Name - opentwig ๐ŸŒฟ",
74
+ "url": "https://links.yourwebsite.com",
75
+ "text": "Share"
76
+ }
77
+ }
78
+ ```
79
+
80
+ ### Configuration Options
81
+
82
+ | Option | Type | Description |
83
+ |--------|------|-------------|
84
+ | `theme` | string | Theme name (`default`, `dark`, `minimal`, `colorful`) |
85
+ | `url` | string | Your page URL (for Open Graph and QR codes) |
86
+ | `title` | string | Page title (appears in browser tab) |
87
+ | `name` | string | Your display name |
88
+ | `content` | string | Bio/description text |
89
+ | `minify` | boolean | Enable CSS minification (default: `true`) |
90
+ | `avatar` | object | Avatar image configuration |
91
+ | `avatar.path` | string | Path to your avatar image |
92
+ | `links` | array | Array of link objects with `url` and `title` |
93
+ | `footerLinks` | array | Footer links (can be URLs or modal dialogs) |
94
+ | `share` | object | Web Share API configuration |
95
+
96
+ ## ๐ŸŽจ Themes
97
+
98
+ OpenTwig includes 4 beautiful themes:
99
+
100
+ - **Default**: Clean, modern design with subtle shadows and rounded corners
101
+ - **Dark**: Dark mode variant of the default theme
102
+ - **Minimal**: Simplified, minimalist design
103
+ - **Colorful**: Vibrant color scheme
104
+
105
+ All themes are mobile-responsive and include:
106
+ - Custom avatar display
107
+ - Link buttons with hover effects
108
+ - Modal dialogs for rich content
109
+ - QR code integration
110
+ - Share button functionality
111
+
112
+ ## ๐Ÿ› ๏ธ CLI Commands
113
+
114
+ ```bash
115
+ # Show help information
116
+ npx opentwig --help
117
+
118
+ # Create sample config.json
119
+ npx opentwig --init
120
+
121
+ # Generate page from config.json
122
+ npx opentwig
123
+ ```
124
+
125
+ ## ๐Ÿ“ Output Files
126
+
127
+ OpenTwig generates the following files in the `dist/` directory:
128
+
129
+ - `index.html` - Main HTML page
130
+ - `style.css` - Processed and optimized CSS
131
+ - `avatar.png` - Processed avatar image
132
+ - `og-image.jpg` - Open Graph image for social sharing
133
+ - `qr.svg` - QR code for mobile sharing
134
+
135
+ ## ๐Ÿ”ง Development
136
+
137
+ ### Project Structure
138
+
139
+ ```
140
+ opentwig/
141
+ โ”œโ”€โ”€ src/
142
+ โ”‚ โ”œโ”€โ”€ index.js # Main entry point
143
+ โ”‚ โ”œโ”€โ”€ constants.js # Application constants
144
+ โ”‚ โ””โ”€โ”€ utils/ # Utility functions
145
+ โ”‚ โ”œโ”€โ”€ buildPage.js # Page building logic
146
+ โ”‚ โ”œโ”€โ”€ generateHTML.js # HTML generation
147
+ โ”‚ โ”œโ”€โ”€ generateOGImage.js # Open Graph image creation
148
+ โ”‚ โ”œโ”€โ”€ generateQR.js # QR code generation
149
+ โ”‚ โ”œโ”€โ”€ processCSS.js # CSS processing and optimization
150
+ โ”‚ โ””โ”€โ”€ ...
151
+ โ”œโ”€โ”€ theme/
152
+ โ”‚ โ”œโ”€โ”€ default/ # Default theme
153
+ โ”‚ โ”‚ โ”œโ”€โ”€ index.js # Theme template
154
+ โ”‚ โ”‚ โ”œโ”€โ”€ style.css # Theme styles
155
+ โ”‚ โ”‚ โ””โ”€โ”€ components/ # Reusable components
156
+ โ”‚ โ”œโ”€โ”€ dark/ # Dark theme
157
+ โ”‚ โ”œโ”€โ”€ minimal/ # Minimal theme
158
+ โ”‚ โ””โ”€โ”€ colorful/ # Colorful theme
159
+ โ””โ”€โ”€ dist/ # Generated output
160
+ ```
161
+
162
+ ### Key Features Implementation
163
+
164
+ - **Image Processing**: Uses Sharp for avatar and OG image processing
165
+ - **QR Code Generation**: Uses qrcode library for SVG QR codes
166
+ - **CSS Processing**: PostCSS with autoprefixer and minification
167
+ - **HTML Generation**: Component-based template system
168
+ - **Theme System**: Modular theme architecture with component support
169
+
170
+ ## ๐ŸŒ Deployment
171
+
172
+ Since OpenTwig generates static files, you can deploy to any static hosting service:
173
+
174
+ - **GitHub Pages**: Push `dist/` folder to a repository
175
+ - **Netlify**: Drag and drop the `dist/` folder
176
+ - **Vercel**: Connect your repository
177
+ - **AWS S3**: Upload files to an S3 bucket
178
+ - **Any web server**: Upload the `dist/` folder to your server
179
+
180
+ ## ๐Ÿ“ License
181
+
182
+ MIT License - see [LICENSE](LICENSE) file for details
183
+
184
+ ## ๐Ÿค Contributing
185
+
186
+ Contributions are welcome! Please feel free to submit a Pull Request.
187
+
188
+ ## ๐Ÿ”— Links
189
+
190
+ - [GitHub Repository](https://github.com/tufantunc/opentwig)
191
+ - [NPM Package](https://www.npmjs.com/package/opentwig)
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "opentwig",
3
+ "version": "1.0.0",
4
+ "description": "opentwig ๐ŸŒฟ is an open source link in bio page generator.",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "opentwig": "./src/index.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node src/index.js"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/tufantunc/opentwig.git"
15
+ },
16
+ "keywords": [
17
+ "link-in-bio",
18
+ "bio-page",
19
+ "linktree",
20
+ "landing-page",
21
+ "static-site",
22
+ "generator",
23
+ "cli",
24
+ "npx",
25
+ "open-source"
26
+ ],
27
+ "author": "Tufan Tunรง",
28
+ "license": "MIT",
29
+ "type": "commonjs",
30
+ "engines": {
31
+ "node": ">=14.0.0"
32
+ },
33
+ "preferGlobal": true,
34
+ "bugs": {
35
+ "url": "https://github.com/tufantunc/opentwig/issues"
36
+ },
37
+ "homepage": "https://github.com/tufantunc/opentwig#readme",
38
+ "dependencies": {
39
+ "autoprefixer": "^10.4.21",
40
+ "html-minifier": "^4.0.0",
41
+ "postcss": "^8.5.6",
42
+ "postcss-minify": "^1.2.0",
43
+ "qrcode": "^1.5.4",
44
+ "sharp": "^0.34.4"
45
+ },
46
+ "browserslist": [
47
+ "ie 9"
48
+ ]
49
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Constants for OpenTwig
3
+ * Centralized constants to avoid magic strings and improve maintainability
4
+ */
5
+
6
+ const CONSTANTS = {
7
+ // File paths
8
+ CONFIG_FILE: 'config.json',
9
+ OUTPUT_DIR: 'dist',
10
+
11
+ // Output files
12
+ OUTPUT_FILES: {
13
+ HTML: 'index.html',
14
+ CSS: 'style.css',
15
+ AVATAR: 'avatar.png',
16
+ OG_IMAGE: 'og-image.jpg',
17
+ QR_CODE: 'qr.svg'
18
+ },
19
+
20
+ // Supported themes
21
+ SUPPORTED_THEMES: ['default', 'dark', 'minimal', 'colorful'],
22
+ DEFAULT_THEME: 'default',
23
+
24
+ // CLI options
25
+ CLI_OPTIONS: {
26
+ HELP: ['--help', '-h'],
27
+ INIT: ['--init', '-i']
28
+ },
29
+
30
+ // Messages
31
+ MESSAGES: {
32
+ CONFIG_NOT_FOUND: 'Config file not found',
33
+ CONFIG_EXISTS: 'config.json already exists. Use --force to overwrite.',
34
+ CONFIG_CREATED: 'Sample config.json created successfully!',
35
+ CONFIG_EDIT_INSTRUCTIONS: 'Edit config.json with your information and run "npx opentwig" to generate your page.',
36
+ PAGE_GENERATED: '๐ŸŽ‰ Page generated successfully!',
37
+ UNKNOWN_OPTION: 'Unknown option:',
38
+ USE_HELP: 'Use --help for usage information.',
39
+ ERROR_PREFIX: 'โŒ Error:',
40
+ SUCCESS_PREFIX: 'โœ…',
41
+ WARNING_PREFIX: 'โš ๏ธ',
42
+ INFO_PREFIX: 'โ„น๏ธ'
43
+ },
44
+
45
+ // URLs
46
+ GITHUB_URL: 'https://github.com/tufantunc/opentwig',
47
+
48
+ // Default values
49
+ DEFAULT_TITLE: 'OpenTwig ๐ŸŒฟ',
50
+ DEFAULT_NAME: 'Your Name',
51
+ DEFAULT_CONTENT: 'Hello World! Here is my bio.',
52
+ DEFAULT_URL: 'https://links.yourwebsite.com',
53
+
54
+ // Required fields for validation
55
+ REQUIRED_FIELDS: ['url', 'name']
56
+ };
57
+
58
+ module.exports = CONSTANTS;
package/src/index.js ADDED
@@ -0,0 +1,36 @@
1
+ #! /usr/bin/env node
2
+ 'use strict';
3
+
4
+ const loadConfig = require('./utils/loadConfig');
5
+ const saveFiles = require('./utils/saveFiles');
6
+ const parseArgs = require('./utils/parseArgs');
7
+ const buildPage = require('./utils/buildPage');
8
+ const CONSTANTS = require('./constants');
9
+
10
+ /**
11
+ * Main application function with proper error handling
12
+ */
13
+ const main = async () => {
14
+ try {
15
+ // Parse CLI arguments first
16
+ parseArgs();
17
+
18
+ // Load and validate configuration
19
+ const config = loadConfig();
20
+
21
+ // Build all page components
22
+ const { html, css, ogImage, qrImage } = await buildPage(config);
23
+
24
+ // Save all generated files
25
+ saveFiles(html, css, config.avatar, ogImage, qrImage);
26
+
27
+ // Success message
28
+ console.log(CONSTANTS.MESSAGES.PAGE_GENERATED);
29
+
30
+ } catch (error) {
31
+ console.error(`${CONSTANTS.MESSAGES.ERROR_PREFIX} ${error.message}`);
32
+ process.exit(1);
33
+ }
34
+ };
35
+
36
+ main();
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Build page components
3
+ * Centralizes the page building logic for better organization
4
+ */
5
+
6
+ const loadTheme = require('./loadTheme');
7
+ const generateHTML = require('./generateHTML');
8
+ const processCSS = require('./processCSS');
9
+ const generateOGImage = require('./generateOGImage');
10
+ const generateQR = require('./generateQR');
11
+
12
+ /**
13
+ * Build all page components from configuration
14
+ * @param {Object} config - The configuration object
15
+ * @returns {Promise<Object>} - Object containing all generated components
16
+ */
17
+ const buildPage = async (config) => {
18
+ try {
19
+ // Load theme
20
+ const theme = loadTheme(config);
21
+
22
+ // Generate HTML
23
+ const html = generateHTML(config, theme);
24
+
25
+ // Process CSS (async)
26
+ const css = await processCSS(config);
27
+
28
+ // Generate OG Image (async)
29
+ const ogImage = await generateOGImage(config);
30
+
31
+ // Generate QR Code (async)
32
+ const qrImage = await generateQR(config.url);
33
+
34
+ return {
35
+ html,
36
+ css,
37
+ ogImage,
38
+ qrImage,
39
+ theme
40
+ };
41
+ } catch (error) {
42
+ throw new Error(`Failed to build page: ${error.message}`);
43
+ }
44
+ };
45
+
46
+ module.exports = buildPage;
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Default configuration values for OpenTwig
3
+ * This centralizes all default settings to make them easier to maintain
4
+ */
5
+
6
+ const DEFAULT_CONFIG = {
7
+ // Theme settings
8
+ theme: 'default',
9
+
10
+ // Page settings
11
+ title: 'OpenTwig ๐ŸŒฟ',
12
+ minify: true,
13
+
14
+ // Content settings
15
+ name: 'Your Name',
16
+ content: 'Hello World! Here is my bio.',
17
+ url: 'https://links.yourwebsite.com',
18
+
19
+ // Avatar settings
20
+ avatar: {
21
+ path: './avatar.png'
22
+ },
23
+
24
+ // Links settings
25
+ links: [],
26
+ footerLinks: [],
27
+
28
+ // Share settings
29
+ share: {
30
+ title: 'Your Name - opentwig ๐ŸŒฟ',
31
+ url: 'https://links.yourwebsite.com',
32
+ text: 'Share'
33
+ }
34
+ };
35
+
36
+ /**
37
+ * Sample configuration for --init command
38
+ * This provides a more complete example with sample data
39
+ */
40
+ const SAMPLE_CONFIG = {
41
+ theme: 'default',
42
+ url: 'https://links.yourwebsite.com',
43
+ title: 'Your Name - opentwig ๐ŸŒฟ',
44
+ name: 'Your Name',
45
+ content: 'Hello World! Here is my bio.',
46
+ minify: true,
47
+ avatar: {
48
+ path: 'avatar.png'
49
+ },
50
+ links: [
51
+ {
52
+ url: 'https://twitter.com',
53
+ title: 'Twitter'
54
+ },
55
+ {
56
+ url: 'https://instagram.com',
57
+ title: 'Instagram'
58
+ },
59
+ {
60
+ url: 'https://linkedin.com',
61
+ title: 'LinkedIn'
62
+ },
63
+ {
64
+ url: 'https://github.com',
65
+ title: 'GitHub'
66
+ },
67
+ {
68
+ url: 'https://youtube.com',
69
+ title: 'YouTube'
70
+ }
71
+ ],
72
+ footerLinks: [
73
+ {
74
+ title: 'Contact',
75
+ url: 'mailto:mail@mail.com'
76
+ },
77
+ {
78
+ title: 'Privacy',
79
+ content: 'When you visit a website, it may store or retrieve information on your browser, mostly in the form of cookies. But its not violating your privacy.'
80
+ }
81
+ ],
82
+ share: {
83
+ title: 'Your Name - opentwig ๐ŸŒฟ',
84
+ url: 'https://links.yourwebsite.com',
85
+ text: 'Share'
86
+ }
87
+ };
88
+
89
+ /**
90
+ * Apply default values to a configuration object
91
+ * @param {Object} config - The configuration object to apply defaults to
92
+ * @returns {Object} - Configuration with defaults applied
93
+ */
94
+ const applyDefaults = (config) => {
95
+ const result = { ...config };
96
+
97
+ // Apply defaults for each key
98
+ Object.keys(DEFAULT_CONFIG).forEach(key => {
99
+ if (result[key] === undefined || result[key] === null) {
100
+ result[key] = DEFAULT_CONFIG[key];
101
+ }
102
+ });
103
+
104
+ // Special handling for nested objects
105
+ if (result.avatar && typeof result.avatar === 'object') {
106
+ result.avatar = { ...DEFAULT_CONFIG.avatar, ...result.avatar };
107
+ } else if (!result.avatar) {
108
+ result.avatar = { ...DEFAULT_CONFIG.avatar };
109
+ }
110
+
111
+ if (result.share && typeof result.share === 'object') {
112
+ result.share = { ...DEFAULT_CONFIG.share, ...result.share };
113
+ } else if (!result.share) {
114
+ result.share = { ...DEFAULT_CONFIG.share };
115
+ }
116
+
117
+ return result;
118
+ };
119
+
120
+ module.exports = {
121
+ DEFAULT_CONFIG,
122
+ SAMPLE_CONFIG,
123
+ applyDefaults
124
+ };
@@ -0,0 +1,23 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { SAMPLE_CONFIG } = require('./configDefaults');
4
+ const CONSTANTS = require('../constants');
5
+
6
+ module.exports = function createSampleConfig() {
7
+ const sampleConfig = SAMPLE_CONFIG;
8
+ const configPath = path.join(process.cwd(), CONSTANTS.CONFIG_FILE);
9
+
10
+ if (fs.existsSync(configPath)) {
11
+ console.log(`${CONSTANTS.MESSAGES.WARNING_PREFIX} ${CONSTANTS.MESSAGES.CONFIG_EXISTS}`);
12
+ return;
13
+ }
14
+
15
+ try {
16
+ fs.writeFileSync(configPath, JSON.stringify(sampleConfig, null, 4));
17
+ console.log(`${CONSTANTS.MESSAGES.SUCCESS_PREFIX} ${CONSTANTS.MESSAGES.CONFIG_CREATED}`);
18
+ console.log(`๐Ÿ“ ${CONSTANTS.MESSAGES.CONFIG_EDIT_INSTRUCTIONS}`);
19
+ } catch (error) {
20
+ console.error(`${CONSTANTS.MESSAGES.ERROR_PREFIX} creating config.json: ${error.message}`);
21
+ process.exit(1);
22
+ }
23
+ };
@@ -0,0 +1,21 @@
1
+ const htmlMinifier = require('html-minifier');
2
+
3
+ module.exports = function(config, theme) {
4
+ let html = theme(config);
5
+
6
+ if (config.minify) {
7
+ const minifiedHtml = htmlMinifier.minify(html, {
8
+ removeComments: true,
9
+ collapseWhitespace: true,
10
+ minifyCSS: true,
11
+ minifyJS: true
12
+ });
13
+ html = minifiedHtml;
14
+ }
15
+
16
+ // Add created by comment after minification
17
+ const createdByComment = '<!-- Created by OpenTwig ๐ŸŒฟ - https://github.com/tufantunc/opentwig -->';
18
+ html = createdByComment + '\n' + html;
19
+
20
+ return html;
21
+ }
@@ -0,0 +1,45 @@
1
+ const sharp = require('sharp');
2
+ const readImageAsBase64 = require('./readImageAsBase64');
3
+
4
+ module.exports = async function({name, content, avatar}) {
5
+ const avatarBase64 = readImageAsBase64(avatar.path);
6
+ // Create SVG with the same dimensions and styling as the original HTML
7
+ const svg = `
8
+ <svg width="1200" height="630" xmlns="http://www.w3.org/2000/svg">
9
+ <defs>
10
+ <style>
11
+ .name-text {
12
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
13
+ font-weight: 700;
14
+ font-size: 22px;
15
+ fill: #fdfdfd;
16
+ }
17
+ .content-text {
18
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
19
+ font-size: 14px;
20
+ fill: #fdfdfd;
21
+ }
22
+ </style>
23
+ </defs>
24
+
25
+ <!-- Background -->
26
+ <rect width="1200" height="630" fill="#2d2d2d"/>
27
+
28
+ <!-- Avatar circle background -->
29
+ <circle cx="552" cy="315" r="48" fill="#dedede"/>
30
+
31
+ <!-- Avatar image (if provided) -->
32
+ ${avatarBase64 ? `<image href="${avatarBase64}" x="504" y="267" width="96" height="96" clip-path="circle(48px at 48px 48px)"/>` : ''}
33
+
34
+ <!-- Text content -->
35
+ <text x="660" y="300" class="name-text">${name}</text>
36
+ <text x="660" y="325" class="content-text">${content}</text>
37
+ </svg>
38
+ `;
39
+
40
+ // Convert SVG to JPG
41
+ const jpgBuffer = await sharp(Buffer.from(svg))
42
+ .jpeg({ quality: 90 })
43
+ .toBuffer();
44
+ return jpgBuffer;
45
+ }
@@ -0,0 +1,10 @@
1
+ const QRCode = require('qrcode');
2
+
3
+ module.exports = async function(url) {
4
+ const svgString = await QRCode.toString(url, { type: 'svg' });
5
+
6
+ // Remove the white background fill from the SVG
7
+ const svgWithoutBackground = svgString.replace(/fill="#ffffff"/g, 'fill="transparent"');
8
+
9
+ return svgWithoutBackground;
10
+ }
@@ -0,0 +1,18 @@
1
+ const path = require('path');
2
+ const cwd = process.cwd();
3
+ const fs = require('fs');
4
+ const { applyDefaults } = require('./configDefaults');
5
+ const CONSTANTS = require('../constants');
6
+
7
+ module.exports = function() {
8
+ const configPath = path.join(cwd, CONSTANTS.CONFIG_FILE);
9
+ if (!fs.existsSync(configPath)) {
10
+ console.error(`${CONSTANTS.MESSAGES.CONFIG_NOT_FOUND}: ${configPath}`);
11
+ process.exit(1);
12
+ }
13
+
14
+ const config = require(configPath);
15
+
16
+ // Apply default values to the loaded configuration
17
+ return applyDefaults(config);
18
+ }
@@ -0,0 +1,23 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ module.exports = function(config) {
5
+ // Try package directory first (for NPX), then current directory (for local dev)
6
+ const packageDir = path.dirname(require.main.filename);
7
+ const currentDir = process.cwd();
8
+
9
+ const themePaths = [
10
+ path.join(packageDir, '..', 'theme', config.theme, 'index.js'), // NPX package
11
+ path.join(currentDir, 'theme', config.theme, 'index.js') // Local development
12
+ ];
13
+
14
+ const themePath = themePaths.find(p => fs.existsSync(p));
15
+
16
+ if (!themePath) {
17
+ console.error(`Theme '${config.theme}' not found in any of these locations:`);
18
+ themePaths.forEach(p => console.error(` - ${p}`));
19
+ process.exit(1);
20
+ }
21
+
22
+ return require(themePath);
23
+ }