@stratawp/cli 0.2.2 → 0.4.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
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2024 Jon Imms
5
+
6
+ This program is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ This program is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU General Public License for more details.
15
+
16
+ You should have received a copy of the GNU General Public License
17
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
18
+
19
+ ---
20
+
21
+ For the full GPL-3.0 license text, see: https://www.gnu.org/licenses/gpl-3.0.txt
package/dist/create.js CHANGED
@@ -7,6 +7,7 @@ import ora from "ora";
7
7
  import { execa } from "execa";
8
8
  import fs from "fs-extra";
9
9
  import path from "path";
10
+ import os from "os";
10
11
  import validatePackageName from "validate-npm-package-name";
11
12
  async function main() {
12
13
  console.log(chalk.bold.cyan("\n\u26A1 Create StrataWP Theme\n"));
@@ -41,6 +42,34 @@ async function main() {
41
42
  name: "author",
42
43
  message: "Author name:"
43
44
  },
45
+ {
46
+ type: "select",
47
+ name: "template",
48
+ message: "Choose a starting template:",
49
+ choices: [
50
+ {
51
+ title: "Basic Theme (Recommended)",
52
+ description: "Simple starter with essential blocks and clean structure",
53
+ value: "basic"
54
+ },
55
+ {
56
+ title: "Advanced Theme",
57
+ description: "Portfolio features, team members, advanced blocks",
58
+ value: "advanced"
59
+ },
60
+ {
61
+ title: "Store Theme",
62
+ description: "WooCommerce ready with product features",
63
+ value: "store"
64
+ },
65
+ {
66
+ title: "Minimal",
67
+ description: "Start from scratch with minimal setup",
68
+ value: "minimal"
69
+ }
70
+ ],
71
+ initial: 0
72
+ },
44
73
  {
45
74
  type: "select",
46
75
  name: "cssFramework",
@@ -87,12 +116,30 @@ async function createTheme(config) {
87
116
  spinner.fail(`Directory ${config.slug} already exists`);
88
117
  process.exit(1);
89
118
  }
90
- await fs.ensureDir(themePath);
91
- spinner.text = "Copying template files...";
92
- await createBasicStructure(themePath, config);
119
+ if (config.template === "minimal") {
120
+ await fs.ensureDir(themePath);
121
+ spinner.text = "Creating basic structure...";
122
+ await createBasicStructure(themePath, config);
123
+ } else {
124
+ spinner.text = `Downloading ${config.template} theme template...`;
125
+ const degit = (await import("degit")).default;
126
+ const templateMap = {
127
+ basic: "JonImmsWordpressDev/StrataWP/examples/basic-theme",
128
+ advanced: "JonImmsWordpressDev/StrataWP/examples/advanced-theme",
129
+ store: "JonImmsWordpressDev/StrataWP/examples/store-theme"
130
+ };
131
+ const emitter = degit(templateMap[config.template], {
132
+ cache: false,
133
+ force: true
134
+ });
135
+ await emitter.clone(themePath);
136
+ spinner.text = "Customizing theme...";
137
+ await customizeTheme(themePath, config);
138
+ }
93
139
  spinner.text = "Installing dependencies...";
94
140
  await execa("pnpm", ["install"], { cwd: themePath });
95
141
  spinner.succeed(chalk.green("Theme created successfully!"));
142
+ await offerWordPressLinking(themePath, config.slug);
96
143
  console.log(chalk.cyan("\n\u{1F4E6} Next steps:\n"));
97
144
  console.log(` cd ${config.slug}`);
98
145
  console.log(" pnpm dev");
@@ -103,6 +150,127 @@ async function createTheme(config) {
103
150
  process.exit(1);
104
151
  }
105
152
  }
153
+ async function customizeTheme(themePath, config) {
154
+ const styleCssPath = path.join(themePath, "style.css");
155
+ if (await fs.pathExists(styleCssPath)) {
156
+ let styleContent = await fs.readFile(styleCssPath, "utf-8");
157
+ styleContent = styleContent.replace(/Theme Name:.*$/m, `Theme Name: ${config.name}`).replace(/Description:.*$/m, `Description: ${config.description}`).replace(/Author:.*$/m, `Author: ${config.author}`).replace(/Text Domain:.*$/m, `Text Domain: ${config.slug}`);
158
+ await fs.writeFile(styleCssPath, styleContent);
159
+ }
160
+ const packageJsonPath = path.join(themePath, "package.json");
161
+ if (await fs.pathExists(packageJsonPath)) {
162
+ const packageJson = await fs.readJson(packageJsonPath);
163
+ packageJson.name = config.slug;
164
+ packageJson.description = config.description;
165
+ packageJson.author = config.author;
166
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
167
+ }
168
+ const readmePath = path.join(themePath, "README.md");
169
+ if (await fs.pathExists(readmePath)) {
170
+ let readmeContent = await fs.readFile(readmePath, "utf-8");
171
+ readmeContent = readmeContent.replace(/^#\s+.*$/m, `# ${config.name}`);
172
+ await fs.writeFile(readmePath, readmeContent);
173
+ }
174
+ const viteConfigPath = path.join(themePath, "vite.config.ts");
175
+ if (await fs.pathExists(viteConfigPath)) {
176
+ let viteConfig = await fs.readFile(viteConfigPath, "utf-8");
177
+ viteConfig = viteConfig.replace(/namespace:\s*['"][\w-]+['"]/, `namespace: '${config.slug}'`);
178
+ await fs.writeFile(viteConfigPath, viteConfig);
179
+ }
180
+ }
181
+ async function offerWordPressLinking(themePath, slug) {
182
+ console.log();
183
+ const { shouldLink } = await prompts({
184
+ type: "confirm",
185
+ name: "shouldLink",
186
+ message: "Would you like to link this theme to a WordPress installation?",
187
+ initial: true
188
+ });
189
+ if (!shouldLink) {
190
+ return;
191
+ }
192
+ const wordpressSites = await detectWordPressSites();
193
+ if (wordpressSites.length === 0) {
194
+ console.log(chalk.yellow("\n\u26A0\uFE0F No WordPress installations detected automatically."));
195
+ console.log(chalk.dim("You can manually create a symlink later:\n"));
196
+ console.log(chalk.dim(` ln -s "${themePath}" /path/to/wordpress/wp-content/themes/${slug}
197
+ `));
198
+ return;
199
+ }
200
+ const { selectedSite } = await prompts({
201
+ type: "select",
202
+ name: "selectedSite",
203
+ message: "Select WordPress installation:",
204
+ choices: wordpressSites.map((site) => ({
205
+ title: `${site.name} (${site.type})`,
206
+ description: site.path,
207
+ value: site
208
+ }))
209
+ });
210
+ if (!selectedSite) {
211
+ return;
212
+ }
213
+ try {
214
+ const targetPath = path.join(selectedSite.path, "wp-content", "themes", slug);
215
+ if (await fs.pathExists(targetPath)) {
216
+ console.log(chalk.yellow(`
217
+ \u26A0\uFE0F Theme already exists at ${targetPath}`));
218
+ return;
219
+ }
220
+ await fs.ensureSymlink(themePath, targetPath);
221
+ console.log(chalk.green(`
222
+ \u2713 Linked theme to ${selectedSite.name}`));
223
+ console.log(chalk.dim(` ${targetPath}`));
224
+ } catch (error) {
225
+ console.log(chalk.red("\n\u2716 Failed to create symlink"));
226
+ console.log(chalk.dim("You can manually create it:\n"));
227
+ console.log(chalk.dim(` ln -s "${themePath}" ${path.join(selectedSite.path, "wp-content", "themes", slug)}
228
+ `));
229
+ }
230
+ }
231
+ async function detectWordPressSites() {
232
+ const sites = [];
233
+ const homeDir = os.homedir();
234
+ const localSitesPath = path.join(homeDir, "Local Sites");
235
+ if (await fs.pathExists(localSitesPath)) {
236
+ try {
237
+ const localDirs = await fs.readdir(localSitesPath);
238
+ for (const dir of localDirs) {
239
+ const sitePath = path.join(localSitesPath, dir, "app", "public");
240
+ const wpConfigPath = path.join(sitePath, "wp-config.php");
241
+ if (await fs.pathExists(wpConfigPath)) {
242
+ sites.push({
243
+ name: dir,
244
+ path: sitePath,
245
+ type: "Local by Flywheel"
246
+ });
247
+ }
248
+ }
249
+ } catch (error) {
250
+ }
251
+ }
252
+ if (process.platform === "darwin") {
253
+ const mampPath = "/Applications/MAMP/htdocs";
254
+ if (await fs.pathExists(mampPath)) {
255
+ try {
256
+ const mampDirs = await fs.readdir(mampPath);
257
+ for (const dir of mampDirs) {
258
+ const sitePath = path.join(mampPath, dir);
259
+ const wpConfigPath = path.join(sitePath, "wp-config.php");
260
+ if (await fs.pathExists(wpConfigPath)) {
261
+ sites.push({
262
+ name: dir,
263
+ path: sitePath,
264
+ type: "MAMP"
265
+ });
266
+ }
267
+ }
268
+ } catch (error) {
269
+ }
270
+ }
271
+ }
272
+ return sites;
273
+ }
106
274
  async function createBasicStructure(themePath, config) {
107
275
  const packageJson = {
108
276
  name: config.slug,
package/dist/index.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import chalk10 from "chalk";
6
5
 
7
6
  // src/commands/dev.ts
8
7
  import chalk from "chalk";
@@ -957,6 +956,20 @@ async function createCSSEntry(framework, cwd) {
957
956
  }
958
957
 
959
958
  // src/index.ts
959
+ import {
960
+ setupCommand,
961
+ generateCommand,
962
+ reviewCommand,
963
+ documentCommand
964
+ } from "@stratawp/ai";
965
+ import {
966
+ searchCommand,
967
+ installCommand,
968
+ infoCommand,
969
+ publishCommand,
970
+ listCommand
971
+ } from "@stratawp/registry";
972
+ import { startCommand as explorerStartCommand } from "@stratawp/explorer";
960
973
  var program = new Command();
961
974
  program.name("stratawp").description("\u26A1 A modern WordPress theme framework").version("0.2.2");
962
975
  program.command("dev").description("Start development server with hot reload").option("-p, --port <port>", "Port number", "3000").option("-h, --host <host>", "Host address", "localhost").action(devCommand);
@@ -967,17 +980,40 @@ program.command("template:new <name>").description("Create a new WordPress templ
967
980
  program.command("part:new <name>").description("Create a new template part").option("-t, --type <type>", "Part type (header|footer|sidebar|content|custom)", "custom").option("--markup <markup>", "Markup style (html|php)", "php").action(partCommand);
968
981
  program.command("design-system:setup <framework>").description("Set up a design system (tailwind|unocss)").action(designSystemCommand);
969
982
  program.command("test").description("Run tests").option("--unit", "Run unit tests only").option("--e2e", "Run E2E tests only").option("--watch", "Watch mode").action(testCommand);
970
- program.command("storybook").description("Launch component explorer").action(() => {
971
- console.log(chalk10.cyan("\u{1F3AD} Launching component explorer..."));
972
- console.log(chalk10.dim("Coming soon!"));
983
+ program.command("explorer").description("Launch interactive component explorer").option("-p, --port <port>", "Port number", "3000").option("-h, --host <host>", "Host address", "localhost").option("--no-open", "Do not open browser automatically").action((options) => {
984
+ explorerStartCommand({
985
+ port: options.port ? parseInt(options.port, 10) : 3e3,
986
+ host: options.host,
987
+ open: options.open !== false
988
+ });
973
989
  });
974
- program.command("ai:block <description>").description("Generate block from description using AI").action((description) => {
975
- console.log(chalk10.magenta("\u{1F916} AI Block Generator"));
976
- console.log(chalk10.dim(`Description: ${description}`));
977
- console.log(chalk10.yellow("\u26A0\uFE0F Coming soon!"));
990
+ program.command("storybook").description("Alias for explorer command").option("-p, --port <port>", "Port number", "3000").option("-h, --host <host>", "Host address", "localhost").option("--no-open", "Do not open browser automatically").action((options) => {
991
+ explorerStartCommand({
992
+ port: options.port ? parseInt(options.port, 10) : 3e3,
993
+ host: options.host,
994
+ open: options.open !== false
995
+ });
978
996
  });
979
- program.command("ai:optimize").description("AI-powered performance optimization").action(() => {
980
- console.log(chalk10.magenta("\u{1F916} AI Optimizer"));
981
- console.log(chalk10.yellow("\u26A0\uFE0F Coming soon!"));
997
+ program.command("ai:setup").description("Configure AI providers and API keys").action(setupCommand);
998
+ program.command("ai:generate <type>").description("Generate code with AI (block|component|pattern)").option("-o, --output <path>", "Output file path").action((type, options) => {
999
+ generateCommand({ type, ...options });
982
1000
  });
1001
+ program.command("ai:review <file>").description("Review code for best practices and security").option("-f, --focus <focus>", "Focus area (security|performance|best-practices|all)", "all").action((file, options) => {
1002
+ reviewCommand({ file, ...options });
1003
+ });
1004
+ program.command("ai:document <file>").description("Generate documentation for code").option("-o, --output <path>", "Output file path").option("-f, --format <format>", "Documentation format (markdown|phpdoc|jsdoc)").action((file, options) => {
1005
+ documentCommand({ file, ...options });
1006
+ });
1007
+ program.command("registry:search <query>").description("Search for components in the registry").option("-t, --type <type>", "Filter by component type (block|component|pattern|template)").option("-l, --limit <number>", "Maximum number of results", "20").action((query, options) => {
1008
+ searchCommand(query, {
1009
+ type: options.type,
1010
+ limit: parseInt(options.limit, 10)
1011
+ });
1012
+ });
1013
+ program.command("registry:install <component>").description("Install a component from the registry").option("-v, --version <version>", "Specific version to install").option("-f, --force", "Overwrite if component already exists").option("-d, --dev", "Install as dev dependency").option("--target-dir <dir>", "Custom target directory").action((component, options) => {
1014
+ installCommand(component, options);
1015
+ });
1016
+ program.command("registry:info <component>").description("Get detailed information about a component").action(infoCommand);
1017
+ program.command("registry:publish").description("Publish current component to the registry").option("--tag <tag>", "Publish with a specific tag").option("--access <access>", "Public or restricted access", "public").option("--dry-run", "Test publication without actually publishing").action(publishCommand);
1018
+ program.command("registry:list").description("List installed StrataWP components").action(listCommand);
983
1019
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stratawp/cli",
3
- "version": "0.2.2",
3
+ "version": "0.4.0",
4
4
  "description": "CLI tool for StrataWP - create and manage WordPress themes",
5
5
  "author": "Jon Imms",
6
6
  "license": "GPL-3.0-or-later",
@@ -39,13 +39,6 @@
39
39
  "dist",
40
40
  "templates"
41
41
  ],
42
- "scripts": {
43
- "dev": "tsup --watch",
44
- "build": "tsup",
45
- "test": "vitest",
46
- "lint": "eslint src",
47
- "clean": "rm -rf dist"
48
- },
49
42
  "dependencies": {
50
43
  "chalk": "^5.3.0",
51
44
  "commander": "^11.1.0",
@@ -54,7 +47,10 @@
54
47
  "fs-extra": "^11.2.0",
55
48
  "ora": "^8.0.1",
56
49
  "prompts": "^2.4.2",
57
- "validate-npm-package-name": "^5.0.0"
50
+ "validate-npm-package-name": "^5.0.0",
51
+ "@stratawp/registry": "0.1.0",
52
+ "@stratawp/ai": "0.1.0",
53
+ "@stratawp/explorer": "0.7.0"
58
54
  },
59
55
  "devDependencies": {
60
56
  "@types/fs-extra": "^11.0.4",
@@ -63,5 +59,12 @@
63
59
  "tsup": "^8.0.1",
64
60
  "typescript": "^5.3.3",
65
61
  "vitest": "^1.0.4"
62
+ },
63
+ "scripts": {
64
+ "dev": "tsup --watch",
65
+ "build": "tsup",
66
+ "test": "vitest",
67
+ "lint": "eslint src",
68
+ "clean": "rm -rf dist"
66
69
  }
67
- }
70
+ }