@asd14/vblocks 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,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025, Andrei Dumitrescu
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # vblocks
2
+
3
+ > 📜 POSIX + 📦 Webpack, an unlikely story!
4
+
5
+ Zero-config Webpack wrapper for React applications with opinionated defaults and
6
+ modern toolchain integration.
7
+
8
+ ## Features
9
+
10
+ - **Zero Configuration** - Works out of the box with sensible defaults
11
+ - **Convention over Configuration** - Smart project structure detection
12
+ - **Modern Toolchain** - SWC, TailwindCSS, PostCSS, CSS Modules, MDX support
13
+ - **Flexible Configuration** - Project configs override defaults when present
14
+ - **Development Experience** - Hot reload, React Refresh, bundle analysis
15
+ - **Production Ready** - Optimized builds with code splitting and caching
16
+
17
+ ## Installation
18
+
19
+ ```sh
20
+ npm install --save-dev @asd14/vblocks
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ### Quick Start
26
+
27
+ ```sh
28
+ # Start development server
29
+ npx vblocks serve
30
+
31
+ # Build for production
32
+ npx vblocks build
33
+
34
+ # View webpack configuration
35
+ npx vblocks eject webpack
36
+ ```
37
+
38
+ ### Project Structure
39
+
40
+ VBlocks expects a standard React project structure:
41
+
42
+ ```
43
+ your-project/
44
+ ├── src/
45
+ │ ├── index.tsx # Application entry point
46
+ │ ├── index.html # HTML template
47
+ │ ├── index.css # Global styles (optional)
48
+ │ └── public/
49
+ │ ├── favicon.svg # Favicon (optional)
50
+ │ └── robots.txt # Robots file (optional)
51
+ ├── .env # Environment variables (optional)
52
+ └── dist/ # Build output
53
+ ```
54
+
55
+ ## How It Works
56
+
57
+ VBlocks is a **POSIX shell script wrapper** around Webpack that:
58
+
59
+ 1. **Detects configuration files** - Uses project configs when available, falls
60
+ back to defaults
61
+ 2. **Sets environment variables** - Passes config paths to Webpack via env vars
62
+ 3. **Proxies commands** - `build` and `serve` are passed directly to `webpack`
63
+ with custom parameters
64
+ 4. **Provides configuration inspection** - `eject` outputs configuration content
65
+ for review
66
+
67
+ ## Configuration
68
+
69
+ VBlocks follows a **configuration hierarchy** - project files take precedence
70
+ over defaults:
71
+
72
+ 1. **Project configs** (in your project root) - highest priority
73
+ 2. **Default configs** (from vblocks package) - fallback
74
+
75
+ ### Supported Configuration Files
76
+
77
+ - `webpack.config.js` - Webpack configuration
78
+ - `postcss.config.js` - PostCSS configuration
79
+ - `tailwind.config.js` - TailwindCSS configuration
80
+ - `.swcrc` - SWC configuration
81
+
82
+ ### Configuration Override Example
83
+
84
+ Create any of these files in your project root to customize behavior:
85
+
86
+ ```js
87
+ // tailwind.config.js - customize TailwindCSS
88
+ export default {
89
+ content: ["./src/**/*.{ts,tsx}"],
90
+ theme: {
91
+ extend: {
92
+ colors: {
93
+ primary: "#your-color"
94
+ }
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
100
+ ## Commands
101
+
102
+ ### `vblocks serve`
103
+
104
+ **Proxies to**:
105
+ `npx webpack serve --config $VB_WEBPACK_CONFIG --env entryDir=$PROJECT_DIR ...`
106
+
107
+ Starts development server with:
108
+
109
+ - Hot Module Replacement
110
+ - Automatic browser opening
111
+ - Bundle analyzer (non-opening)
112
+ - Error overlay
113
+ - History API fallback for SPAs
114
+
115
+ ### `vblocks build`
116
+
117
+ **Proxies to**:
118
+ `npx webpack build --config $VB_WEBPACK_CONFIG --env entryDir=$PROJECT_DIR ...`
119
+
120
+ Creates production build with:
121
+
122
+ - Minified assets
123
+ - Content hashing
124
+ - Optimized chunks
125
+ - Performance hints
126
+
127
+ ### `vblocks eject webpack`
128
+
129
+ **Special command** - Outputs the webpack configuration to stdout for
130
+ inspection:
131
+
132
+ ```sh
133
+ npx vblocks eject webpack > webpack.config.js
134
+ ```
135
+
136
+ ## Features in Detail
137
+
138
+ ### CSS Modules Support
139
+
140
+ - **Global CSS** - `import './styles.css'`
141
+ - **Local CSS Modules** - `import styles from './component.module.css'`
142
+ - **camelCase exports** - `.my-class` becomes `styles.myClass`
143
+ - **Development-friendly naming** - includes class names in dev builds
144
+
145
+ ### Asset Handling
146
+
147
+ - **Images** - Automatic optimization, inlined if < 10KB
148
+ - **Fonts** - Copied to `fonts/` directory
149
+ - **Favicon & Robots** - Automatically included in build
150
+
151
+ ### Environment Variables
152
+
153
+ VBlocks automatically loads `.env` files in your project root:
154
+
155
+ ```env
156
+ # .env
157
+ REACT_APP_API_URL=https://api.example.com
158
+ ```
159
+
160
+ Access in your code:
161
+
162
+ ```tsx
163
+ const apiUrl = process.env.REACT_APP_API_URL
164
+ ```
165
+
166
+ ### Path Aliases
167
+
168
+ Built-in alias for cleaner imports:
169
+
170
+ ```tsx
171
+ // Instead of: import { Component } from '../../../components'
172
+ import { Component } from "@self/components"
173
+ ```
174
+
175
+ ## Peer Dependencies
176
+
177
+ ```json
178
+ {
179
+ "peerDependencies": {
180
+ "react": "^18 || ^19",
181
+ "react-dom": "^18 || ^19"
182
+ }
183
+ }
184
+ ```
185
+
186
+ ## License
187
+
188
+ BSD 3-Clause
189
+
package/bin/vblocks.sh ADDED
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env sh
2
+
3
+ # =============================================================================
4
+ # VBLOCKS - Webpack with opinionated configs
5
+ # =============================================================================
6
+ # This script helps JavaScript developers run common Webpack tasks without
7
+ # needing to remember complex command line arguments.
8
+ #
9
+ # Available Commands:
10
+ # ---
11
+ # build: Creates a production-ready build of your project
12
+ # serve: Starts a development server with live reload
13
+ # eject: Copies default webpack and swc configs to your project for customization
14
+ #
15
+ # Usage Examples:
16
+ # ---
17
+ # npx vblocks build # Build for production
18
+ # npx vblocks serve # Start dev server
19
+ # npx vblocks eject # Copy config files to your project
20
+ # =============================================================================
21
+
22
+ # Directory where this script is located and
23
+ # default configuration file paths (from this package).
24
+ export VB_DIR="$(dirname "$(dirname "$(readlink -f "$0")")")"
25
+
26
+ # Project directory (where you run the command from) and
27
+ # project-specific config file paths.
28
+ export PROJECT_DIR="$(pwd)"
29
+
30
+ # Choose between project-specific and default configs. Project files take precedence.
31
+ select_config() {
32
+ project_path="$PROJECT_DIR/$1"
33
+ default_path=$(realpath "$VB_DIR/src/$1")
34
+ [ -f "$project_path" ] && echo "$project_path" || echo "$default_path"
35
+ }
36
+
37
+ # Determine config files location.
38
+ export VB_WEBPACK_CONFIG="$(select_config "webpack.config.js")"
39
+ export VB_POSTCSS_CONFIG="$(select_config "postcss.config.js")"
40
+ export VB_TAILWIND_CONFIG="$(select_config "tailwind.config.js")"
41
+ export VB_SWC_CONFIG="$(select_config ".swcrc")"
42
+
43
+ # Wrap webpack command with custom configs
44
+ run_webpack() {
45
+ action=$1
46
+ shift 1
47
+
48
+ robots_path="$PROJECT_DIR/src/public/robots.txt"
49
+ if [ ! -f "$robots_path" ]; then
50
+ robots_path="$VB_DIR/src/public/robots.txt"
51
+ fi
52
+
53
+ favicon_path="$PROJECT_DIR/src/public/favicon.svg"
54
+ if [ ! -f "$favicon_path" ]; then
55
+ favicon_path="$VB_DIR/src/public/favicon.svg"
56
+ fi
57
+
58
+ npx webpack "$action" --config "$VB_WEBPACK_CONFIG" \
59
+ --env "entryDir=$PROJECT_DIR" \
60
+ --env "robotsPath=$robots_path" \
61
+ --env "faviconPath=$favicon_path" \
62
+ "$@"
63
+ }
64
+
65
+ # Copy config files to your project
66
+ copy_config_files() {
67
+ project_path="$PROJECT_DIR/$1"
68
+ default_path=$(realpath "$VB_DIR/$1")
69
+
70
+ if [ ! -f "$project_path" ]; then
71
+ cp "$default_path" "$project_path" \
72
+ && echo "Created $1 in your project" >&2
73
+ fi
74
+ }
75
+
76
+ print_hero() {
77
+ version=$(node -p "require('$VB_DIR/package.json').version")
78
+ bold='\033[1m'
79
+ green='\033[32m'
80
+ blue='\033[34m'
81
+ gray='\033[90m'
82
+ reset='\033[0m'
83
+
84
+ bold() { echo "${bold}$1${reset}"; }
85
+ blue() { echo "${blue}$1${reset}"; }
86
+ green() { echo "${green}$1${reset}"; }
87
+ gray() { echo "${gray}$1${reset}"; }
88
+ replace_home() { echo "$1" | sed "s|$HOME|~|g"; }
89
+ replace_config_base() { echo "$1" | sed "s|$PROJECT_DIR|\$PROJECT_DIR|g" | sed "s|$VB_DIR|\$VBLOCKS_DIR|g"; }
90
+ format_config_path() {
91
+ if echo "$1" | grep -q "^$VB_DIR"; then
92
+ gray "$(replace_config_base "$1")"
93
+ else
94
+ replace_config_base "$1"
95
+ fi
96
+ }
97
+
98
+ printf "╭───────────┤ %b\n" "$(green " VBlocks v${version}")"
99
+ printf "│ %b\n" "$(bold "󱆃 POSIX +  Webpack, an unlikely story!")"
100
+ printf "│\n"
101
+ printf "│ %b\n" "\$PROJECT_DIR: $(replace_home "$PROJECT_DIR")"
102
+ printf "│ %b\n" "\$VBLOCKS_DIR: $(gray "$(replace_home "$VB_DIR")")"
103
+ printf "│\n"
104
+ printf "│ %b\n" " Webpack: $(format_config_path "$VB_WEBPACK_CONFIG")"
105
+ printf "│ %b\n" " PostCSS: $(format_config_path "$VB_POSTCSS_CONFIG")"
106
+ printf "│ %b\n" " Tailwind: $(format_config_path "$VB_TAILWIND_CONFIG")"
107
+ printf "│ %b\n" "󱡮 SWC: $(format_config_path "$VB_SWC_CONFIG")"
108
+ printf "╰──┤\n\n"
109
+ }
110
+
111
+ # ╭──────────────────────
112
+ # │ Main
113
+ # ╰────────
114
+
115
+ print_hero
116
+
117
+ case "$1" in
118
+ build)
119
+ shift
120
+ run_webpack build "$@"
121
+ ;;
122
+ serve)
123
+ shift
124
+ run_webpack serve "$@"
125
+ ;;
126
+ eject)
127
+ shift
128
+ file=$1
129
+
130
+ case "$file" in
131
+ webpack)
132
+ cat "$VB_DIR/webpack.config.js"
133
+ ;;
134
+ *)
135
+ echo "Unknown config file to eject. Available: webpack, postcss, tailwind and swc" >&2
136
+ exit 1
137
+ ;;
138
+ esac
139
+ ;;
140
+ *)
141
+ echo "Usage: $0 {build|serve|eject}" >&2
142
+ exit 1
143
+ ;;
144
+ esac
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/package.json",
3
+ "name": "@asd14/vblocks",
4
+ "version": "1.0.0",
5
+ "description": "󱆃 POSIX +  Webpack, an unlikely story!",
6
+ "sideEffects": false,
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "nx": {
11
+ "tags": [
12
+ "type:lib",
13
+ "target:react"
14
+ ]
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/asd-xiv/vblocks.git"
19
+ },
20
+ "files": [
21
+ "bin",
22
+ "src"
23
+ ],
24
+ "type": "module",
25
+ "bin": {
26
+ "vblocks": "bin/vblocks.sh"
27
+ },
28
+ "scripts": {
29
+ "----UTIL": "",
30
+ "prepare": "git config --local core.hooksPath .githook",
31
+ "format": "eslint --fix .",
32
+ "----CODE-ANALYSIS": "",
33
+ "lint": "eslint .",
34
+ "typecheck": "tsc --noEmit"
35
+ },
36
+ "dependencies": {
37
+ "@csstools/postcss-global-data": "^3.1.0",
38
+ "@mdx-js/loader": "^3.1.1",
39
+ "@pmmmwh/react-refresh-webpack-plugin": "^0.6.1",
40
+ "browserslist": "^4.26.2",
41
+ "copy-webpack-plugin": "^13.0.1",
42
+ "css-loader": "^7.1.2",
43
+ "cssnano": "^7.1.1",
44
+ "dotenv-webpack": "^8.1.1",
45
+ "html-webpack-plugin": "^5.6.4",
46
+ "mini-css-extract-plugin": "^2.9.4",
47
+ "postcss": "^8.5.6",
48
+ "postcss-loader": "^8.2.0",
49
+ "postcss-preset-env": "^10.3.1",
50
+ "react-refresh": "^0.17.0",
51
+ "style-loader": "^4.0.0",
52
+ "swc-loader": "^0.2.6",
53
+ "tailwindcss": "^4.1.13",
54
+ "webpack": "^5.101.3",
55
+ "webpack-bundle-analyzer": "^4.10.2",
56
+ "webpack-cli": "^6.0.1",
57
+ "webpack-dev-server": "^5.2.2"
58
+ },
59
+ "devDependencies": {
60
+ "@asd14/eslint-config": "^14.4.0",
61
+ "@asd14/prettier-config": "^1.1.0",
62
+ "@asd14/ts-config": "^1.1.0",
63
+ "@commitlint/cli": "^19.8.1",
64
+ "@commitlint/config-conventional": "^19.8.1",
65
+ "@semantic-release/git": "^10.0.1",
66
+ "@types/dotenv-webpack": "^7.0.8",
67
+ "@types/webpack-bundle-analyzer": "^4.7.0",
68
+ "conventional-changelog-conventionalcommits": "^9.1.0",
69
+ "eslint": "^9.35.0",
70
+ "prettier": "^3.6.2",
71
+ "semantic-release": "^24.2.8",
72
+ "typescript": "^5.9.2"
73
+ },
74
+ "peerDependencies": {
75
+ "react": "^18 || ^19",
76
+ "react-dom": "^18 || ^19"
77
+ },
78
+ "engines": {
79
+ "node": ">=20"
80
+ }
81
+ }
package/src/.swcrc ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "$schema": "https://swc.rs/schema.json",
3
+ "minify": false,
4
+ "sourceMaps": true,
5
+ "env": {
6
+ "mode": "entry",
7
+ "coreJs": "3.22",
8
+ "targets": "> 0.25%, not dead"
9
+ },
10
+ "jsc": {
11
+ "externalHelpers": true,
12
+ "minify": {
13
+ "format": {
14
+ "comments": "all"
15
+ }
16
+ },
17
+ "parser": {
18
+ "syntax": "typescript",
19
+ "tsx": true,
20
+ "dynamicImport": true
21
+ },
22
+ "transform": {
23
+ "react": {
24
+ "runtime": "automatic"
25
+ }
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,13 @@
1
+ export default {
2
+ plugins: {
3
+ "postcss-preset-env": {
4
+ stage: 2,
5
+ },
6
+ "tailwindcss": {
7
+ config: process.env["VB_TAILWIND_CONFIG"],
8
+ },
9
+ // "cssnano": {
10
+ // preset: "default",
11
+ // },
12
+ },
13
+ }
@@ -0,0 +1,101 @@
1
+ <svg width="16" height="16" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M67.383 47.174c1.41-2.643-4.043-7.336-10.345-3.056a18.404 18.404 0 0 0-2.217 1.938c-1.799 1.841-4.052 3.204-5.736 1.183-1.683-2.022-.681-8.124.204-11.333l-6.473-2.008c-.144 1.312-.918 4.034-1.391 3.992-.473-.041-.051-1.038.05-2.16.103-1.123-8.406 2.893-8.828 3.097-.315.153-4.386-1.113-6.63.334-.747.482-3.024 4.057-3.224 4.104 0 0-.514-4.308-.987-6.38-.483-2.069-6.432 6.788-5.671 8.976 1.293 3.738.436 16.439 6.923 22.04 7.507 6.478 21.219 4.646 26.176-1.103 4.957-5.75 3.269-8.75 7.818-13.197 3.853-3.77 8.578-4.387 10.33-6.427Z" fill="url(#a)"/>
3
+ <path d="M21.797 36.884c-.474-2.073-1.405-7.725-2.254-10.744s-2.606-4.243-5.06-3.343c-2.452.9-1.177 6.482-.95 7.85.348 2.092 1.846 13.03 2.602 15.214" fill="url(#b)"/>
4
+ <path d="M32.642 38.827c-2.003.019-3.139-2.513-3.848-6.116-.543-2.75-1.827-10.28-2.653-14.226-1.024-4.892-2.188-6.06-4.595-5.88-2.406.176-2.986 3.26-2.397 6.496.496 2.736 1.47 7.016 2.773 12.353.779 3.2 1.67 11.616.867 11.81" fill="url(#c)"/>
5
+ <path d="M49.29 35.906c.88-3.21 2.666-14.695 3.366-17.742.7-3.046-.909-4.808-2.88-5.244-1.975-.436-3.658.728-4.804 6.807-.723 3.835-1.242 7.257-2.156 14.17l3.524 5.24 2.95-3.231Z" fill="url(#d)"/>
6
+ <path d="M41.471 35.73c.103-1.122 2.013-11.866 2.365-14.236.742-4.952 2.782-12.195-1.58-13.36-2.81-.746-3.859 1.8-4.184 2.945-.324 1.146-2.244 14.375-2.35 15.58-.112 1.206-1.892 11.737-3.08 12.168" fill="url(#e)"/>
7
+ <path d="M47.189 13.732c-.9 1.006-1.646 2.958-2.217 5.995-.723 3.835-1.196 7.267-2.156 14.171-.18 1.308-.918 4.034-1.391 3.993 2.856-1.299 3.195-11.607 3.909-16.295.756-4.98 2.527-8.62 1.855-7.864Z" fill="url(#f)"/>
8
+ <path d="M35.722 26.66c-.112 1.205-1.79 11.587-3.08 12.167 0 0 1.61 1.284 2.602-5.022.84-5.337 2.198-14.968 3.288-18.914 1.024-3.72.714-6.07.338-5.555-.375.514-.672 1.303-.797 1.743-.32 1.146-2.24 14.375-2.351 15.58Z" fill="url(#g)"/>
9
+ <path d="M19.144 19.101c.496 2.736 1.47 7.016 2.773 12.353.78 3.2 1.67 11.616.867 11.81 4.234-.639-4.326-25.577-2.712-30.024-1.15 1.095-1.368 3.436-.928 5.861Z" fill="url(#h)"/>
10
+ <path d="M26.995 27.406c1.256 6.566 1.558 11.792 5.647 11.421-1.6 0-3.028-1.813-3.848-6.116-.524-2.755-1.8-10.285-2.653-14.226-.908-4.178-1.595-5.245-3.26-5.801 3.39 3.997 2.55 6.552 4.114 14.722Z" fill="url(#i)"/>
11
+ <path d="M42.352 23.205c-.672 5.027-2.133 13.916-1.07 14.63-.372-.241.097-1.104.19-2.105.101-1.123 2.012-11.867 2.364-14.236.464-3.074 1.651-7.87.83-10.846.098 3.607-1.766 8.486-2.314 12.557Z" fill="url(#j)"/>
12
+ <path d="M48.149 43.9c-.019-2.068.607-6.051 1.14-7.994.881-3.209 2.667-14.695 3.367-17.741.213-.927.213-1.734.05-2.425-.32 1.41-1.256 2.898-1.543 5.694-.464 4.484-2.3 11.463-2.93 13.86-.631 2.397-.682 5.198-.084 8.606Z" fill="url(#k)"/>
13
+ <path d="M36.58 71.963c.927-2.782.774-5.091-2.023-5.458-7.618-.992-13.27-7.233-15.821-18.858-2.277-10.364-5.328-18.966-4.535-24.711-2.059 1.224-.886 6.4-.668 7.711.348 2.092 1.846 13.03 2.602 15.214 1.293 3.738.435 16.439 6.923 22.04 3.621 3.126 8.69 4.313 13.521 4.062Z" fill="url(#l)"/>
14
+ <path d="M15.105 36.513a.533.533 0 0 1 .01-.06c4.808 15.2 6.232 24.117 14.383 33.72 1.15 1.355-1.293 1.28-3.278-.19a15.25 15.25 0 0 1-3.162-2.086c-6.492-5.597-5.63-18.298-6.923-22.035-.404-1.155-.446-6.627-1.03-9.349Z" fill="url(#m)"/>
15
+ <path d="M53.801 53.146c1.164 1.396-2.74 7.29-5.704 9.757-2.962 2.467-6.306 3.33-7.47 1.933-1.164-1.395 1.475-4.901 3.775-7.994 3.97-5.337 8.235-5.091 9.4-3.696Z" fill="url(#n)"/>
16
+ <path opacity=".75" d="M20.79 51.82c-1.289 1.549 2.63 10.484 6.497 12.733 3.867 2.25 8.713 1.558 9.659-.227.946-1.79-3.075-4.567-6.362-7.646-5.676-5.31-8.505-6.409-9.794-4.86Z" fill="url(#o)"/>
17
+ <path d="M22.784 43.265s-.552-4.48-.987-6.38c-.474-2.073-1.406-7.726-2.254-10.744-.376-1.327-.923-2.305-1.628-2.912.594 1.446 1.623 4.256 2.537 10.335 1.474 10.707 2.207 9.808 2.332 9.701Z" fill="url(#p)"/>
18
+ <path d="M57.038 44.118a18.41 18.41 0 0 0-2.217 1.938c-1.799 1.841-4.052 3.205-5.736 1.183-.663-.798-.927-1.99-.936-3.339-.979 6.826 2.75 6.06 5.453 4.08 2.703-1.98 5.166-5.721 10.262-4.905.918.148 1.78.491 2.42.75-1.84-1.58-5.384-2.332-9.246.293Z" fill="url(#q)"/>
19
+ <path d="M49.234 66.798c4.957-5.75 3.269-8.75 7.818-13.197 3.853-3.766 8.574-4.382 10.33-6.427.242-.45.27-.96.14-1.475-1.025 1.818-3.849 1.985-7.1 3.747-4.697 2.55-6.932 4.841-8.972 10.113-2.04 5.273-8.648 7.749-13.086 6.993l10.87.246Z" fill="url(#r)"/>
20
+ <path d="M13.144 24.698c-.427 2.036.232 5.004.39 5.95.347 2.09 1.845 13.025 2.6 15.214 1.294 3.737.436 16.438 6.924 22.04 7.507 6.477 21.219 4.646 26.176-1.104 4.957-5.746 3.269-8.746 7.822-13.197 3.854-3.765 8.574-4.382 10.332-6.427l.514-1.048c-.195-.167-.347-.32-.37-.427-1.025 1.818-3.427 3.306-7.1 3.747-4.368.529-6.932 3.32-8.973 8.592-2.04 5.273-8.648 9.274-13.085 8.514-4.438-.76-14.996 3.635-18.558-14.695 0-.004 0-.01-.004-.014-.135-1.215-.325-2.703-.677-3.695-2.44-6.868-3.756-15.78-4.234-19.694-.102-.765.023-3.366-1.757-3.756Z" fill="url(#s)"/>
21
+ <defs>
22
+ <linearGradient id="a" x1="30.522" y1="78.112" x2="38.65" y2="54.832" gradientUnits="userSpaceOnUse">
23
+ <stop offset=".001" stop-color="#FFCB4B"/>
24
+ <stop offset="1" stop-color="#FFD748"/>
25
+ </linearGradient>
26
+ <linearGradient id="b" x1="13.791" y1="25.984" x2="19.015" y2="38.812" gradientUnits="userSpaceOnUse">
27
+ <stop offset=".001" stop-color="#FFCB4B"/>
28
+ <stop offset="1" stop-color="#FFD748"/>
29
+ </linearGradient>
30
+ <linearGradient id="c" x1="24.855" y1="9.606" x2="26.229" y2="36.681" gradientUnits="userSpaceOnUse">
31
+ <stop offset=".001" stop-color="#FFCB4B"/>
32
+ <stop offset="1" stop-color="#FFD748"/>
33
+ </linearGradient>
34
+ <linearGradient id="d" x1="50.191" y1="17.877" x2="44.062" y2="37.022" gradientUnits="userSpaceOnUse">
35
+ <stop offset=".001" stop-color="#FFCB4B"/>
36
+ <stop offset="1" stop-color="#FFD748"/>
37
+ </linearGradient>
38
+ <linearGradient id="e" x1="39.842" y1="12.37" x2="38.113" y2="35.48" gradientUnits="userSpaceOnUse">
39
+ <stop offset=".001" stop-color="#FFCB4B"/>
40
+ <stop offset="1" stop-color="#FFD748"/>
41
+ </linearGradient>
42
+ <linearGradient id="f" x1="47.865" y1="26.215" x2="42.855" y2="25.565" gradientUnits="userSpaceOnUse">
43
+ <stop stop-color="#FFBC47" stop-opacity="0"/>
44
+ <stop offset="1" stop-color="#E68E43"/>
45
+ </linearGradient>
46
+ <linearGradient id="g" x1="61.309" y1="23.553" x2="27.344" y2="24.286" gradientUnits="userSpaceOnUse">
47
+ <stop stop-color="#FFBC47" stop-opacity="0"/>
48
+ <stop offset="1" stop-color="#E68E43"/>
49
+ </linearGradient>
50
+ <linearGradient id="h" x1="27.596" y1="26.884" x2="19.898" y2="28.594" gradientUnits="userSpaceOnUse">
51
+ <stop offset=".312" stop-color="#FFBC47" stop-opacity="0"/>
52
+ <stop offset="1" stop-color="#FF8900"/>
53
+ </linearGradient>
54
+ <linearGradient id="i" x1="21.146" y1="27.55" x2="29.607" y2="25.255" gradientUnits="userSpaceOnUse">
55
+ <stop stop-color="#FFBC47" stop-opacity="0"/>
56
+ <stop offset="1" stop-color="#E68E43"/>
57
+ </linearGradient>
58
+ <linearGradient id="j" x1="40.083" y1="23.586" x2="43.128" y2="24.278" gradientUnits="userSpaceOnUse">
59
+ <stop stop-color="#FFBC47" stop-opacity="0"/>
60
+ <stop offset="1" stop-color="#E68E43"/>
61
+ </linearGradient>
62
+ <linearGradient id="k" x1="48.021" y1="29.318" x2="53.4" y2="30.442" gradientUnits="userSpaceOnUse">
63
+ <stop stop-color="#FFBC47" stop-opacity="0"/>
64
+ <stop offset="1" stop-color="#E68E43"/>
65
+ </linearGradient>
66
+ <linearGradient id="l" x1="22.939" y1="47.935" x2="14.651" y2="49.574" gradientUnits="userSpaceOnUse">
67
+ <stop offset=".312" stop-color="#FFBC47" stop-opacity="0"/>
68
+ <stop offset="1" stop-color="#FF8900"/>
69
+ </linearGradient>
70
+ <linearGradient id="p" x1="17.728" y1="33.665" x2="22.322" y2="32.972" gradientUnits="userSpaceOnUse">
71
+ <stop stop-color="#FFBC47" stop-opacity="0"/>
72
+ <stop offset="1" stop-color="#E68E43"/>
73
+ </linearGradient>
74
+ <linearGradient id="r" x1="48.277" y1="55.314" x2="86.286" y2="61.942" gradientUnits="userSpaceOnUse">
75
+ <stop stop-color="#C86F34" stop-opacity="0"/>
76
+ <stop offset="1" stop-color="#C86F34"/>
77
+ </linearGradient>
78
+ <radialGradient id="m" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(21.99252 -5.09153 12.92251 55.81797 23.826 62.543)">
79
+ <stop stop-color="#FFBC47" stop-opacity="0"/>
80
+ <stop offset="1" stop-color="#FF8900"/>
81
+ </radialGradient>
82
+ <radialGradient id="n" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(6.7248 -5.59609 2.59761 3.12154 47.15 58.8)">
83
+ <stop stop-color="#FBE07A"/>
84
+ <stop offset="1" stop-color="#FFD748" stop-opacity="0"/>
85
+ </radialGradient>
86
+ <radialGradient id="o" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-8.49254 -7.39156 3.37243 -3.87476 28.435 59.523)">
87
+ <stop stop-color="#FBE07A"/>
88
+ <stop offset=".265" stop-color="#FCDF73" stop-opacity=".735"/>
89
+ <stop offset=".655" stop-color="#FDDB5F" stop-opacity=".345"/>
90
+ <stop offset="1" stop-color="#FFD748" stop-opacity="0"/>
91
+ </radialGradient>
92
+ <radialGradient id="q" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="rotate(9.433 -182.762 356.699) scale(26.9129)">
93
+ <stop stop-color="#FFBC47" stop-opacity="0"/>
94
+ <stop offset="1" stop-color="#FFA754"/>
95
+ </radialGradient>
96
+ <radialGradient id="s" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(32.442 50.116) scale(56.5998)">
97
+ <stop offset=".521" stop-color="#FFBC47" stop-opacity="0"/>
98
+ <stop offset="1" stop-color="#FF8900"/>
99
+ </radialGradient>
100
+ </defs>
101
+ </svg>
@@ -0,0 +1,4 @@
1
+ User-agent: *
2
+ Allow: /
3
+ Crawl-delay: 10
4
+ Sitemap: /sitemap.xml
@@ -0,0 +1,4 @@
1
+ /** @type {import("tailwindcss").Config} */
2
+ export default {
3
+ content: ["./src/**/*.{ts,tsx}"],
4
+ }
@@ -0,0 +1,311 @@
1
+ /** @typedef {import("webpack").Configuration} WebpackConfiguration */
2
+
3
+ import fs from "node:fs"
4
+ import "webpack-dev-server"
5
+ import ReactRefreshWebpackPlugin from "@pmmmwh/react-refresh-webpack-plugin"
6
+ import CopyPlugin from "copy-webpack-plugin"
7
+ import DotenvPlugin from "dotenv-webpack"
8
+ import HtmlWebPackPlugin from "html-webpack-plugin"
9
+ import MiniCssExtractPlugin from "mini-css-extract-plugin"
10
+ import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer"
11
+
12
+ const VB_SWC_CONFIG = process.env["VB_SWC_CONFIG"]
13
+ const VB_POSTCONFIG_PATH = process.env["VB_POSTCSS_CONFIG"]
14
+
15
+ if (!VB_SWC_CONFIG)
16
+ throw new Error("VB_SWC_CONFIG environment variable is not defined")
17
+
18
+ if (!VB_POSTCONFIG_PATH)
19
+ throw new Error("VB_POSTCSS_CONFIG environment variable is not defined")
20
+
21
+ /**
22
+ * Load, parse and provide targets defaults for SWC configuration
23
+ */
24
+ const loadSwcConfig = () => {
25
+ const content = fs.readFileSync(VB_SWC_CONFIG, "utf8")
26
+ const config = /** @type {unknown} */ (JSON.parse(content))
27
+ const typedConfig = /** @type {import("@swc/core").Config} */ (config)
28
+
29
+ return {
30
+ ...typedConfig,
31
+ env: {
32
+ targets: "> 0.25, not dead",
33
+ ...typedConfig.env,
34
+ },
35
+ }
36
+ }
37
+
38
+ /**
39
+ * CSS module configuration shared between global and local styles
40
+ *
41
+ * @param {boolean} isProduction
42
+ */
43
+ const createCssModuleConfig = isProduction => ({
44
+ // import styles from './styles.module.css'
45
+ // instead of
46
+ // import * as styles from './styles.module.css'
47
+ namedExport: false,
48
+
49
+ // Use camelCase when referring to CSS classes in JavaScript code
50
+ // Example: styles.myClass in JS maps to .my-class in CSS
51
+ exportLocalsConvention: "camelCase",
52
+
53
+ // Use a unique hash in production, but include class names
54
+ // in development for easier debugging
55
+ localIdentName: isProduction ? "[hash:base64:8]" : "[local]__[hash:base64:5]",
56
+ })
57
+
58
+ /**
59
+ * @typedef {Object} VBlocksEnvironment
60
+ *
61
+ * @property {string} entryDir - Target directory from where vblocks cmd was called
62
+ * @property {string} robotsPath - Path to robots.txt copied to the output directory
63
+ * @property {string} faviconPath - Path to favicon.ico copied to the output directory
64
+ */
65
+
66
+ /**
67
+ * @typedef {Object} VBlocksProps
68
+ *
69
+ * @property {"production"|"development"} mode
70
+ */
71
+
72
+ /**
73
+ * @param {VBlocksEnvironment} environment
74
+ * @param {VBlocksProps} props
75
+ * @returns {WebpackConfiguration}
76
+ */
77
+ export default ({ entryDir, robotsPath, faviconPath }, { mode }) => {
78
+ const isProduction = mode === "production"
79
+ const isServe = process.argv[2] == "serve"
80
+ const swcConfig = loadSwcConfig()
81
+ const commonPostCSSConfigs = createCssModuleConfig(isProduction)
82
+
83
+ const paths = {
84
+ appEntry: `${entryDir}/src/index.tsx`,
85
+ globalCssEntry: `${entryDir}/src/index.css`,
86
+ output: `${entryDir}/dist`,
87
+ htmlTemplate: `${entryDir}/src/index.html`,
88
+ favicon: faviconPath,
89
+ robots: robotsPath,
90
+ dotenv: `${entryDir}/.env`,
91
+ }
92
+
93
+ return {
94
+ // The order of entries matters.
95
+ // The global CSS file needs to be first in the array to ensure its
96
+ // styles are loaded before any component-specific styles.
97
+ entry: [
98
+ ...(fs.existsSync(paths.globalCssEntry) ? [paths.globalCssEntry] : []),
99
+ paths.appEntry,
100
+ ],
101
+
102
+ mode: mode,
103
+
104
+ output: {
105
+ publicPath: "/",
106
+ path: paths.output,
107
+ // Cannot use 'contenthash' when hot reloading is enabled.
108
+ filename: isServe ? "js/[name].js" : "js/[name].[contenthash].js",
109
+ // Delete existing files in the output directory before building
110
+ clean: true,
111
+ },
112
+
113
+ devtool: isServe ? "eval-source-map" : false,
114
+
115
+ // Both Webpack and SWC should use the same browserslist configuration
116
+ // with .swcrc as source of truth
117
+ target: `browserslist:${swcConfig.env.targets}`,
118
+
119
+ module: {
120
+ rules: [
121
+ {
122
+ test: /\.(ts|tsx)$/,
123
+ exclude: /node_modules/,
124
+ use: {
125
+ loader: "swc-loader",
126
+ options: swcConfig,
127
+ },
128
+ },
129
+ {
130
+ test: /\.mdx?$/,
131
+ exclude: /node_modules/,
132
+ use: [
133
+ { loader: "swc-loader", options: swcConfig },
134
+ { loader: "@mdx-js/loader" },
135
+ ],
136
+ },
137
+ {
138
+ test: /\.module\.css$/,
139
+ use: [
140
+ {
141
+ loader: MiniCssExtractPlugin.loader,
142
+ },
143
+ {
144
+ loader: "css-loader",
145
+ options: {
146
+ // Enable/disable @import resolving.
147
+ import: true,
148
+
149
+ // Apply "postcss-loader" on imported css files before
150
+ // processing them with "css-loader"
151
+ importLoaders: 1,
152
+
153
+ modules: {
154
+ ...commonPostCSSConfigs,
155
+ mode: "local",
156
+ },
157
+ },
158
+ },
159
+ {
160
+ loader: "postcss-loader",
161
+ options: {
162
+ postcssOptions: {
163
+ config: VB_POSTCONFIG_PATH,
164
+ },
165
+ },
166
+ },
167
+ ],
168
+ },
169
+ {
170
+ test: /\.css$/,
171
+ exclude: /\.module\.css$/,
172
+ use: [
173
+ {
174
+ loader: MiniCssExtractPlugin.loader,
175
+ },
176
+ {
177
+ loader: "css-loader",
178
+ options: {
179
+ // Enable/disable @import resolving.
180
+ import: true,
181
+
182
+ // Apply "postcss-loader" on imported css files before
183
+ // processing them with "css-loader"
184
+ importLoaders: 1,
185
+
186
+ modules: {
187
+ ...commonPostCSSConfigs,
188
+ mode: "global",
189
+ },
190
+ },
191
+ },
192
+ {
193
+ loader: "postcss-loader",
194
+ options: {
195
+ postcssOptions: {
196
+ config: VB_POSTCONFIG_PATH,
197
+ },
198
+ },
199
+ },
200
+ ],
201
+ },
202
+ {
203
+ test: /\.(jpg|gif|png|svg)$/,
204
+ type: "asset",
205
+ parser: {
206
+ dataUrlCondition: {
207
+ maxSize: 10 * 1024,
208
+ },
209
+ },
210
+ },
211
+ {
212
+ test: /\.(woff|woff2|ttf|eot)$/,
213
+ type: "asset/resource",
214
+ generator: {
215
+ filename: "fonts/[name][ext]",
216
+ },
217
+ },
218
+ ],
219
+ },
220
+
221
+ // https://webpack.js.org/configuration/dev-server/
222
+ devServer: isServe
223
+ ? {
224
+ compress: true,
225
+ // When using the HTML5 History API, the index.html page will likely
226
+ // have to be served in place of any 404 responses.
227
+ historyApiFallback: true,
228
+
229
+ // Hot Module Replacement (HMR) exchanges, adds, or removes modules
230
+ // while an application is running, without a full reload.
231
+ // https://webpack.js.org/concepts/hot-module-replacement/
232
+ hot: true,
233
+
234
+ // Tells dev-server to open the browser after server had been started.
235
+ open: true,
236
+ client: {
237
+ overlay: true,
238
+ },
239
+ }
240
+ : undefined,
241
+
242
+ plugins: [
243
+ new HtmlWebPackPlugin({
244
+ template: paths.htmlTemplate,
245
+ favicon: paths.favicon,
246
+ }),
247
+
248
+ new MiniCssExtractPlugin({
249
+ filename: isServe ? "css/[name].css" : "css/[name].[contenthash].css",
250
+ }),
251
+
252
+ new DotenvPlugin({
253
+ path: paths.dotenv,
254
+ }),
255
+
256
+ new CopyPlugin({
257
+ patterns: [
258
+ {
259
+ from: paths.robots,
260
+ to: "robots.txt",
261
+ },
262
+ ],
263
+ }),
264
+
265
+ isServe &&
266
+ new BundleAnalyzerPlugin({
267
+ openAnalyzer: false,
268
+ }),
269
+
270
+ isServe && new ReactRefreshWebpackPlugin(),
271
+ ].filter(Boolean),
272
+
273
+ resolve: {
274
+ extensions: [".tsx", ".ts", ".jsx", ".js"],
275
+ alias: {
276
+ "@self": `${entryDir}/src`,
277
+ },
278
+ },
279
+
280
+ optimization: {
281
+ // Enable tree shaking for unused exports
282
+ usedExports: true,
283
+
284
+ // Merge runtime.js into main.js for fewer HTTP request
285
+ runtimeChunk: false,
286
+
287
+ // Extract node_module dependencies into a separate chunk for better caching
288
+ splitChunks: {
289
+ chunks: "all",
290
+ cacheGroups: {
291
+ react: {
292
+ test: /[/\\]node_modules[/\\]react(-dom)?[/\\]/,
293
+ name: "react",
294
+ chunks: "all",
295
+ priority: 10,
296
+ },
297
+ vendors: {
298
+ test: /[/\\]node_modules[/\\]/,
299
+ name: "vendors",
300
+ chunks: "all",
301
+ priority: 5,
302
+ },
303
+ },
304
+ },
305
+ },
306
+
307
+ performance: {
308
+ hints: isProduction ? "warning" : false,
309
+ },
310
+ }
311
+ }