@webmate-studio/cli 0.1.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.
@@ -0,0 +1,163 @@
1
+ import * as esbuild from 'esbuild';
2
+ import esbuildSvelte from 'esbuild-svelte';
3
+ import fs from 'fs/promises';
4
+ import path from 'path';
5
+ import pc from 'picocolors';
6
+
7
+ /**
8
+ * Bundle island JavaScript files with esbuild
9
+ * Supports: Vanilla JS, React, Preact, Svelte, Alpine, Lit, Vue, Solid
10
+ */
11
+ export async function bundleIsland(islandPath, outputPath, options = {}) {
12
+ const {
13
+ minify = true,
14
+ sourcemap = true,
15
+ target = 'es2020',
16
+ format = 'esm'
17
+ } = options;
18
+
19
+ try {
20
+ // Resolve paths for dependencies from CLI package
21
+ const cliNodeModules = path.resolve(import.meta.dirname, '../../node_modules');
22
+
23
+ // Determine if this file should use JSX loader
24
+ // Only use JSX for .jsx files (React/Preact), not for .js files (Lit/Alpine/Vue/Vanilla)
25
+ const useJsxLoader = islandPath.endsWith('.jsx');
26
+
27
+ const result = await esbuild.build({
28
+ entryPoints: [islandPath],
29
+ bundle: true,
30
+ minify,
31
+ sourcemap,
32
+ format,
33
+ target,
34
+ outfile: outputPath,
35
+ platform: 'browser',
36
+ // Only enable JSX for .jsx files (React/Preact)
37
+ loader: useJsxLoader ? {
38
+ '.jsx': 'jsx',
39
+ '.ts': 'tsx',
40
+ '.tsx': 'tsx'
41
+ } : {
42
+ '.ts': 'tsx',
43
+ '.tsx': 'tsx'
44
+ },
45
+ // Use automatic JSX runtime (React 17+) only for JSX files
46
+ jsx: useJsxLoader ? 'automatic' : undefined,
47
+ // JSX import source (Preact uses preact, React uses react)
48
+ jsxImportSource: useJsxLoader ? 'preact' : undefined,
49
+ // Alias vue to the full build (with template compiler)
50
+ alias: {
51
+ 'vue': 'vue/dist/vue.esm-bundler.js'
52
+ },
53
+ // Define Vue feature flags
54
+ define: {
55
+ '__VUE_OPTIONS_API__': 'true',
56
+ '__VUE_PROD_DEVTOOLS__': 'false',
57
+ '__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': 'false'
58
+ },
59
+ plugins: [
60
+ // Svelte support
61
+ esbuildSvelte({
62
+ compilerOptions: {
63
+ css: 'injected' // Inject CSS into JS
64
+ }
65
+ })
66
+ ],
67
+ // Don't bundle browser globals
68
+ external: [],
69
+ // Add CLI's node_modules to resolve path so Svelte runtime can be found
70
+ nodePaths: [cliNodeModules],
71
+ // Log level
72
+ logLevel: 'warning'
73
+ });
74
+
75
+ return {
76
+ success: true,
77
+ outputPath,
78
+ size: (await fs.stat(outputPath)).size,
79
+ warnings: result.warnings
80
+ };
81
+ } catch (error) {
82
+ return {
83
+ success: false,
84
+ error: error.message,
85
+ errors: error.errors
86
+ };
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Bundle all islands in a component directory
92
+ */
93
+ export async function bundleComponentIslands(componentDir, outputDir) {
94
+ const islandsDir = path.join(componentDir, 'islands');
95
+
96
+ // Check if islands directory exists
97
+ try {
98
+ await fs.access(islandsDir);
99
+ } catch {
100
+ // No islands directory
101
+ return { islands: [], success: true };
102
+ }
103
+
104
+ // Find all .js files in islands directory
105
+ const files = await fs.readdir(islandsDir);
106
+ const islandFiles = files.filter((f) => f.endsWith('.js'));
107
+
108
+ if (islandFiles.length === 0) {
109
+ return { islands: [], success: true };
110
+ }
111
+
112
+ // Create output directory
113
+ await fs.mkdir(outputDir, { recursive: true });
114
+
115
+ const results = [];
116
+
117
+ for (const islandFile of islandFiles) {
118
+ const inputPath = path.join(islandsDir, islandFile);
119
+ const outputPath = path.join(outputDir, islandFile);
120
+
121
+ console.log(pc.dim(` Bundling ${islandFile}...`));
122
+
123
+ const result = await bundleIsland(inputPath, outputPath);
124
+
125
+ if (result.success) {
126
+ const sizeKb = (result.size / 1024).toFixed(2);
127
+ console.log(pc.green(` ✓ ${islandFile} → ${sizeKb}kb`));
128
+ } else {
129
+ console.log(pc.red(` ✗ ${islandFile} failed:`));
130
+ console.log(pc.red(` ${result.error}`));
131
+ }
132
+
133
+ results.push({
134
+ file: islandFile,
135
+ ...result
136
+ });
137
+ }
138
+
139
+ const allSuccess = results.every((r) => r.success);
140
+
141
+ return {
142
+ islands: results,
143
+ success: allSuccess
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Get total bundle size for all islands
149
+ */
150
+ export function getTotalBundleSize(bundleResults) {
151
+ return bundleResults.islands.reduce((total, island) => {
152
+ return total + (island.size || 0);
153
+ }, 0);
154
+ }
155
+
156
+ /**
157
+ * Format bundle size in human-readable format
158
+ */
159
+ export function formatSize(bytes) {
160
+ if (bytes < 1024) return `${bytes}B`;
161
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)}KB`;
162
+ return `${(bytes / (1024 * 1024)).toFixed(2)}MB`;
163
+ }
@@ -0,0 +1,103 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { pathToFileURL } from 'url';
4
+
5
+ /**
6
+ * Load Webmate config from current project
7
+ */
8
+ export async function loadConfig() {
9
+ const configPath = join(process.cwd(), 'wm.config.js');
10
+
11
+ // If no config file exists, return defaults
12
+ if (!existsSync(configPath)) {
13
+ return getDefaultConfig();
14
+ }
15
+
16
+ try {
17
+ // Dynamic import with file URL
18
+ const configUrl = pathToFileURL(configPath).href;
19
+ const module = await import(configUrl);
20
+ const config = module.default || module;
21
+
22
+ // Merge with defaults
23
+ return {
24
+ ...getDefaultConfig(),
25
+ ...config
26
+ };
27
+ } catch (error) {
28
+ console.warn(`Warning: Could not load config from ${configPath}`);
29
+ console.warn(error.message);
30
+ return getDefaultConfig();
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Get default configuration
36
+ */
37
+ function getDefaultConfig() {
38
+ return {
39
+ version: '0.1.0', // Default version if not specified
40
+ components: {
41
+ path: './components',
42
+ styles: [],
43
+ fonts: [],
44
+ islands: {
45
+ path: './islands',
46
+ framework: 'lit'
47
+ }
48
+ },
49
+ preview: {
50
+ port: 5173,
51
+ theme: 'light',
52
+ viewport: {
53
+ width: 1440,
54
+ height: 900
55
+ },
56
+ backgrounds: ['#ffffff', '#f5f5f5', '#000000']
57
+ },
58
+ output: {
59
+ dir: './dist',
60
+ format: 'esm',
61
+ minify: false
62
+ }
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Get path to config file
68
+ * @returns {string} Path to wm.config.js
69
+ */
70
+ export function getConfigPath() {
71
+ return join(process.cwd(), 'wm.config.js');
72
+ }
73
+
74
+ /**
75
+ * Update version in config file
76
+ * @param {string} newVersion - New version string
77
+ */
78
+ export function updateConfigVersion(newVersion) {
79
+ const configPath = getConfigPath();
80
+
81
+ if (!existsSync(configPath)) {
82
+ throw new Error('No wm.config.js found in current directory.');
83
+ }
84
+
85
+ let content = readFileSync(configPath, 'utf8');
86
+
87
+ // Check if version field exists
88
+ if (content.includes('version:')) {
89
+ // Update existing version
90
+ content = content.replace(
91
+ /version:\s*['"][\d.]+['"]/,
92
+ `version: '${newVersion}'`
93
+ );
94
+ } else {
95
+ // Add version field after export default {
96
+ content = content.replace(
97
+ /export default \{/,
98
+ `export default {\n\t// Component version (semantic versioning)\n\tversion: '${newVersion}',\n`
99
+ );
100
+ }
101
+
102
+ writeFileSync(configPath, content, 'utf8');
103
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Semantic Versioning Utilities
3
+ */
4
+
5
+ /**
6
+ * Parse a semantic version string
7
+ * @param {string} version - Version string (e.g., "1.2.3")
8
+ * @returns {{major: number, minor: number, patch: number}}
9
+ */
10
+ export function parseVersion(version) {
11
+ const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
12
+
13
+ if (!match) {
14
+ throw new Error(`Invalid version format: ${version}. Expected format: x.y.z`);
15
+ }
16
+
17
+ return {
18
+ major: parseInt(match[1], 10),
19
+ minor: parseInt(match[2], 10),
20
+ patch: parseInt(match[3], 10)
21
+ };
22
+ }
23
+
24
+ /**
25
+ * Convert version object to string
26
+ * @param {{major: number, minor: number, patch: number}} version
27
+ * @returns {string}
28
+ */
29
+ export function versionToString(version) {
30
+ return `${version.major}.${version.minor}.${version.patch}`;
31
+ }
32
+
33
+ /**
34
+ * Increment patch version (1.2.3 → 1.2.4)
35
+ * @param {string} version - Current version
36
+ * @returns {string} - New version
37
+ */
38
+ export function incrementPatch(version) {
39
+ const parsed = parseVersion(version);
40
+ parsed.patch += 1;
41
+ return versionToString(parsed);
42
+ }
43
+
44
+ /**
45
+ * Increment minor version (1.2.3 → 1.3.0)
46
+ * @param {string} version - Current version
47
+ * @returns {string} - New version
48
+ */
49
+ export function incrementMinor(version) {
50
+ const parsed = parseVersion(version);
51
+ parsed.minor += 1;
52
+ parsed.patch = 0;
53
+ return versionToString(parsed);
54
+ }
55
+
56
+ /**
57
+ * Increment major version (1.2.3 → 2.0.0)
58
+ * @param {string} version - Current version
59
+ * @returns {string} - New version
60
+ */
61
+ export function incrementMajor(version) {
62
+ const parsed = parseVersion(version);
63
+ parsed.major += 1;
64
+ parsed.minor = 0;
65
+ parsed.patch = 0;
66
+ return versionToString(parsed);
67
+ }
68
+
69
+ /**
70
+ * Validate version format
71
+ * @param {string} version - Version to validate
72
+ * @returns {boolean}
73
+ */
74
+ export function isValidVersion(version) {
75
+ return /^\d+\.\d+\.\d+$/.test(version);
76
+ }