@codigodoleo/wp-kit 3.1.1 → 3.3.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.
Files changed (112) hide show
  1. package/lib/commands/init.js +19 -10
  2. package/lib/config/versions.js +5 -5
  3. package/lib/core/infer-ci-capabilities.js +17 -0
  4. package/lib/prompts/index.js +27 -17
  5. package/lib/utils/git.js +50 -13
  6. package/modules/blank-theme/index.js +71 -0
  7. package/modules/blank-theme/lint/.prettierrc.json +12 -0
  8. package/modules/blank-theme/lint/.stylelintrc.json +7 -0
  9. package/modules/{lint → blank-theme/lint}/eslint.config.mjs +2 -8
  10. package/modules/blank-theme/lint/pint.json +11 -0
  11. package/modules/blank-theme/prompts.js +36 -0
  12. package/modules/blank-theme/templates/.lando.yml.hbs +26 -0
  13. package/modules/blank-theme/templates/functions.php.hbs +46 -0
  14. package/modules/blank-theme/templates/package.json.hbs +16 -0
  15. package/modules/blank-theme/templates/style.css.hbs +7 -0
  16. package/modules/blank-theme/templates/vite.config.js.hbs +23 -0
  17. package/modules/deploy/.github/workflows/deploy-docker.yml +104 -0
  18. package/modules/deploy/.github/workflows/deploy-ssh.yml +128 -0
  19. package/modules/deploy/.github/workflows/release.yml +57 -0
  20. package/modules/deploy/index.js +77 -0
  21. package/modules/deploy/prompts.js +1 -3
  22. package/modules/deploy/templates/.github/workflows/ci.yml.hbs +29 -1
  23. package/modules/deploy/templates/.gitlab/ci/deploy-docker.yml.hbs +35 -0
  24. package/modules/deploy/templates/.gitlab/ci/deploy-ssh.yml.hbs +83 -0
  25. package/modules/deploy/templates/.gitlab/ci/lint-blank-theme.yml.hbs +35 -0
  26. package/modules/deploy/templates/.gitlab/ci/lint-mu-plugin.yml.hbs +19 -0
  27. package/modules/deploy/templates/.gitlab/ci/lint-plugin.yml.hbs +19 -0
  28. package/modules/deploy/templates/.gitlab/ci/lint-sage.yml.hbs +47 -0
  29. package/modules/deploy/templates/.gitlab/ci/release.yml.hbs +26 -0
  30. package/modules/git/.husky/commit-msg +8 -4
  31. package/modules/git/.husky/pre-commit +0 -3
  32. package/modules/git/templates/package.json.hbs +4 -4
  33. package/modules/git/templates/workspace.json.hbs +1 -1
  34. package/modules/lint/.commitlintrc.json +21 -0
  35. package/modules/lint/.prettierignore +15 -25
  36. package/modules/lint/.prettierrc.json +5 -16
  37. package/modules/lint/index.js +75 -8
  38. package/modules/lint/prompts.js +1 -8
  39. package/modules/lint/templates/.lando.yml.hbs +3 -8
  40. package/modules/lint/templates/package.json.hbs +4 -4
  41. package/modules/lint/templates/workspace.json.hbs +3 -3
  42. package/modules/mu-plugin/index.js +49 -0
  43. package/modules/mu-plugin/lint/.prettierrc.json +7 -0
  44. package/modules/mu-plugin/lint/eslint.config.mjs +30 -0
  45. package/modules/mu-plugin/lint/pint.json +11 -0
  46. package/modules/mu-plugin/prompts.js +29 -0
  47. package/modules/mu-plugin/templates/.lando.yml.hbs +15 -0
  48. package/modules/mu-plugin/templates/composer.json.hbs +14 -0
  49. package/modules/mu-plugin/templates/plugin.php.hbs +15 -0
  50. package/modules/php/templates/composer.json.hbs +15 -1
  51. package/modules/plugin/index.js +45 -0
  52. package/modules/plugin/lint/.prettierrc.json +7 -0
  53. package/modules/plugin/lint/eslint.config.mjs +30 -0
  54. package/modules/plugin/lint/pint.json +11 -0
  55. package/modules/plugin/prompts.js +29 -0
  56. package/modules/plugin/templates/.lando.yml.hbs +15 -0
  57. package/modules/plugin/templates/composer.json.hbs +14 -0
  58. package/modules/plugin/templates/plugin.php.hbs +15 -0
  59. package/modules/sage/index.js +139 -15
  60. package/modules/sage/lint/.prettierrc.json +12 -0
  61. package/modules/sage/lint/.stylelintrc.json +7 -0
  62. package/modules/sage/lint/eslint.config.mjs +30 -0
  63. package/modules/sage/lint/pint.json +11 -0
  64. package/modules/sage/prompts.js +22 -1
  65. package/modules/sage/templates/.devcontainer/devcontainer.json.hbs +7 -0
  66. package/modules/sage/templates/.devcontainer/docker-compose.override.yml.hbs +6 -0
  67. package/modules/sage/templates/.devcontainer/post-create.sh.hbs +11 -0
  68. package/modules/sage/templates/.lando.yml.hbs +43 -45
  69. package/modules/sage/templates/server/cmd/setup-sage-node.sh.hbs +23 -0
  70. package/modules/sage/templates/server/cmd/setup-sage-php.sh.hbs +24 -0
  71. package/modules/sage/templates/theme/app/Blocks/.gitkeep.hbs +0 -0
  72. package/modules/sage/templates/theme/app/Fields/.gitkeep.hbs +0 -0
  73. package/modules/sage/templates/theme/composer.json.hbs +25 -3
  74. package/modules/sage/templates/theme/package.json.hbs +15 -3
  75. package/modules/sage/templates/theme/resources/views/blocks/.gitkeep.hbs +0 -0
  76. package/modules/sage/templates/theme/vite.config.js.hbs +55 -13
  77. package/modules/sage/templates/workspace.json.hbs +94 -6
  78. package/modules/skills/index.js +18 -0
  79. package/modules/skills/prompts.js +21 -0
  80. package/modules/test/index.js +19 -0
  81. package/modules/test/prompts.js +8 -0
  82. package/modules/test/templates/composer.json.hbs +16 -0
  83. package/modules/test/templates/package.json.hbs +11 -0
  84. package/modules/test/templates/playwright.config.js.hbs +29 -0
  85. package/modules/test/tests/Pest.php +39 -0
  86. package/modules/test/tests/SmokeTest.php +15 -0
  87. package/modules/test/tests/e2e/example.spec.js +11 -0
  88. package/modules/test/tests/e2e/helpers/loginHelper.js +40 -0
  89. package/package.json +1 -1
  90. package/templates/.devcontainer/Dockerfile.hbs +24 -0
  91. package/templates/.devcontainer/config/nginx.conf +23 -0
  92. package/templates/.devcontainer/config/nginx.conf.hbs +23 -0
  93. package/templates/.devcontainer/devcontainer.json.hbs +29 -0
  94. package/templates/.devcontainer/docker-compose.yml.hbs +73 -0
  95. package/templates/.devcontainer/setup.sh.hbs +17 -0
  96. package/templates/.env.hbs +38 -15
  97. package/templates/.gitignore.hbs +84 -13
  98. package/templates/.lando.yml.hbs +61 -17
  99. package/templates/README.md.hbs +23 -7
  100. package/templates/composer.json.hbs +60 -12
  101. package/templates/package.json.hbs +0 -2
  102. package/templates/server/cmd/install-wp.sh.hbs +54 -43
  103. package/templates/server/cmd/setup-node.sh.hbs +11 -0
  104. package/templates/server/cmd/setup-php.sh.hbs +14 -0
  105. package/templates/server/www/vhosts.conf.hbs +54 -17
  106. package/templates/workspace.json.hbs +57 -1
  107. package/templates/wp-cli.yml.hbs +1 -0
  108. package/modules/lint/.eslintignore +0 -36
  109. package/modules/lint/.eslintrc.json +0 -8
  110. package/modules/lint/.stylelintignore +0 -19
  111. package/modules/lint/.stylelintrc.json +0 -9
  112. package/modules/lint/pint.json +0 -26
@@ -9,7 +9,6 @@ import { Generator } from '../core/generator.js';
9
9
  import { log, color } from '../utils/logger.js';
10
10
  import { inferCiCapabilities } from '../core/infer-ci-capabilities.js';
11
11
  import { validateVersionCompatibility } from '../config/versions.js';
12
- import { installSuperpowersSage } from '../utils/git.js';
13
12
 
14
13
  const __filename = fileURLToPath(import.meta.url);
15
14
  const __dirname = path.dirname(__filename);
@@ -20,12 +19,11 @@ export const initCommand = new Command('init')
20
19
  .option('--debug', 'Ativa modo debug para logs detalhados')
21
20
  .action(async ({ output, defaults, debug }) => {
22
21
  const target = path.resolve(output);
23
- const { enabledModules, ...context } = await runPrompts({ defaults });
22
+ const { enabledModules: promptedModules, ...context } = await runPrompts({ defaults });
23
+ let enabledModules = [...promptedModules];
24
24
 
25
25
  // Normalização de chaves para consistência entre módulos
26
- if (context.enable_sage === true) {
27
- context.useSage = true;
28
- }
26
+ context.useSage = context.enable_sage === true;
29
27
  if (context.git_provider && !context.gitProvider) {
30
28
  context.gitProvider = context.git_provider;
31
29
  }
@@ -34,12 +32,12 @@ export const initCommand = new Command('init')
34
32
  if (context.useSage && context.sageVersion === '11') {
35
33
  context.phpVersion = '8.3';
36
34
  context.nodeVersion = '22';
37
- context.wpCoreVersion = '6.8.1';
35
+ context.wpCoreVersion = '6.9.4';
38
36
 
39
37
  log(color.orange, '🔧 Sage 11 selected — automatically adjusting versions:');
40
38
  log(color.orange, ' → PHP: 8.3');
41
39
  log(color.orange, ' → Node: 22');
42
- log(color.orange, ' → WP Core: 6.8.1');
40
+ log(color.orange, ' → WP Core: 6.9.4');
43
41
  }
44
42
 
45
43
  // 📋 Validar compatibilidade de versões (se Sage está habilitado)
@@ -71,6 +69,9 @@ export const initCommand = new Command('init')
71
69
  if (context.ci.theme.paths.themeRoot) {
72
70
  log(color.blue, ` → themeRoot: ${context.ci.theme.paths.themeRoot}`);
73
71
  }
72
+ if (context.useSage) {
73
+ log(color.blue, ' → sageMode: advanced (automatic when Sage is enabled)');
74
+ }
74
75
 
75
76
  const generator = new Generator(target);
76
77
  await fs.ensureDir(target);
@@ -80,6 +81,13 @@ export const initCommand = new Command('init')
80
81
  await generator.generateFile('.env', context);
81
82
  await generator.generateFile('.editorconfig', context);
82
83
  await generator.generateFile('server/cmd/install-wp.sh', context);
84
+ await generator.generateFile('server/cmd/setup-php.sh', context);
85
+ await generator.generateFile('server/cmd/setup-node.sh', context);
86
+ await generator.generateFile('.devcontainer/devcontainer.json', context);
87
+ await generator.generateFile('.devcontainer/docker-compose.yml', context);
88
+ await generator.generateFile('.devcontainer/Dockerfile', context);
89
+ await generator.generateFile('.devcontainer/setup.sh', context);
90
+ await generator.generateFile('.devcontainer/config/nginx.conf', context);
83
91
 
84
92
  await generator.copyFile('.cspell.json');
85
93
 
@@ -93,6 +101,9 @@ export const initCommand = new Command('init')
93
101
 
94
102
  await fs.chmod(path.join(target, 'content/uploads'), 0o777);
95
103
  await fs.chmod(path.join(target, 'server/cmd/install-wp.sh'), 0o755);
104
+ await fs.chmod(path.join(target, 'server/cmd/setup-php.sh'), 0o755);
105
+ await fs.chmod(path.join(target, 'server/cmd/setup-node.sh'), 0o755);
106
+ await fs.chmod(path.join(target, '.devcontainer/setup.sh'), 0o755);
96
107
 
97
108
  await generator.generateFile('server/www/vhosts.conf', context);
98
109
  if (context.useRocket) {
@@ -101,6 +112,7 @@ export const initCommand = new Command('init')
101
112
 
102
113
  await generator.copyFile('wp-config.php');
103
114
  await generator.copyFile('index.php');
115
+ await generator.generateFile('wp-cli.yml', context);
104
116
 
105
117
  log(color.blue, `🧩 Enabled modules: ${enabledModules.join(', ')}`);
106
118
 
@@ -154,8 +166,5 @@ export const initCommand = new Command('init')
154
166
  // Gera CLAUDE.md com overview do projeto e referência ao AI Co-Pilot
155
167
  await generator.generateFile('CLAUDE.md', context);
156
168
 
157
- // Instala superpowers-sage em .claude/ do projeto gerado
158
- await installSuperpowersSage(target);
159
-
160
169
  log(color.green, '✅ Project initialized with templates.');
161
170
  });
@@ -53,8 +53,8 @@ export const VERSION_REGISTRY = {
53
53
  * Only actively supported versions per php.net
54
54
  */
55
55
  php: {
56
- latest: '8.3',
57
- supported: ['8.3', '8.2', '8.1'],
56
+ latest: '8.4',
57
+ supported: ['8.4', '8.3', '8.2', '8.1'],
58
58
  eol: ['8.0', '7.4', '7.3', '7.2'], // Deprecated - do not use
59
59
  description: 'Supported PHP versions. PHP 7.4+ reached EOL on 2022-11-28',
60
60
  // PHP version dates: https://www.php.net/supported-versions.php
@@ -70,9 +70,9 @@ export const VERSION_REGISTRY = {
70
70
  * Tracks LTS and current stable releases
71
71
  */
72
72
  node: {
73
- latest: '22',
74
- lts: ['22', '20', '18'],
75
- all: ['22', '20', '18'],
73
+ latest: '24',
74
+ lts: ['24', '22', '20', '18'],
75
+ all: ['24', '22', '20', '18'],
76
76
  deprecated: ['16', '14', '12', '10'],
77
77
  description: 'Supported Node.js versions - prefer LTS releases',
78
78
  // LTS information: https://nodejs.org/en/about/releases/
@@ -84,6 +84,23 @@ export function inferCiCapabilities(context) {
84
84
  node: hasNode,
85
85
  sage: hasSage,
86
86
  },
87
+ lint: {
88
+ sage:
89
+ hasSage &&
90
+ Boolean(context.enable_lint) &&
91
+ (context.sage_lint_eslint !== false ||
92
+ context.sage_lint_pint !== false ||
93
+ context.sage_lint_stylelint !== false),
94
+ mu_plugin: Boolean(context.enable_mu_plugin && context.enable_lint),
95
+ plugin: Boolean(context.enable_plugin && context.enable_lint),
96
+ blank_theme: Boolean(context.enable_blank_theme && context.enable_lint),
97
+ any:
98
+ Boolean(context.enable_lint) &&
99
+ (hasSage ||
100
+ Boolean(context.enable_mu_plugin) ||
101
+ Boolean(context.enable_plugin) ||
102
+ Boolean(context.enable_blank_theme)),
103
+ },
87
104
  deploy: {
88
105
  docker: context.deploy_strategy === 'docker',
89
106
  },
@@ -6,6 +6,14 @@ import { Generator } from '../core/generator.js';
6
6
 
7
7
  import { loadModulePrompts } from './loadModulePrompts.js';
8
8
 
9
+ function parseBooleanEnv(value) {
10
+ if (typeof value !== 'string') return null;
11
+ const normalized = value.trim().toLowerCase();
12
+ if (['1', 'true', 'yes', 'on'].includes(normalized)) return true;
13
+ if (['0', 'false', 'no', 'off'].includes(normalized)) return false;
14
+ return null;
15
+ }
16
+
9
17
  export async function runPrompts({ defaults = false } = {}) {
10
18
  const defaultsGit = getGitUserDefaults();
11
19
 
@@ -46,20 +54,7 @@ export async function runPrompts({ defaults = false } = {}) {
46
54
  message: 'Repository URL:',
47
55
  default: defaultsGit.gitRepository,
48
56
  },
49
- {
50
- type: 'list',
51
- name: 'phpVersion',
52
- message: 'PHP version:',
53
- choices: ['8.3', '8.2', '8.1'],
54
- default: '8.3',
55
- },
56
- {
57
- type: 'list',
58
- name: 'nodeVersion',
59
- message: 'Node.js version:',
60
- choices: ['22', '20', '18'],
61
- default: '22',
62
- },
57
+ // phpVersion and nodeVersion are locked to '8.4' / '24' — injected into context, not prompted
63
58
  {
64
59
  type: 'list',
65
60
  name: 'wpCoreVersion',
@@ -90,13 +85,26 @@ export async function runPrompts({ defaults = false } = {}) {
90
85
  for (const prompt of allPrompts) {
91
86
  if (prompt.when && !prompt.when(defaultAnswers)) continue;
92
87
  const def = prompt.default;
93
- defaultAnswers[prompt.name] = typeof def === 'function' ? def() : def;
88
+ defaultAnswers[prompt.name] = typeof def === 'function' ? def(defaultAnswers) : def;
89
+ }
90
+
91
+ const envEnableSage = parseBooleanEnv(process.env.WP_KIT_ENABLE_SAGE);
92
+ if (envEnableSage !== null) {
93
+ defaultAnswers.enable_sage = envEnableSage;
94
94
  }
95
+
96
+ const envGitProvider = process.env.WP_KIT_GIT_PROVIDER;
97
+ if (envGitProvider) {
98
+ defaultAnswers.gitProvider = envGitProvider;
99
+ }
100
+
95
101
  const enabledModules = Object.keys(modulePromptDefs).filter(
96
- (mod) => defaultAnswers[`enable_${mod}`] === true
102
+ (mod) => defaultAnswers[`enable_${mod.replace(/-/g, '_')}`] === true
97
103
  );
98
104
  return {
99
105
  ...defaultAnswers,
106
+ phpVersion: '8.4',
107
+ nodeVersion: '24',
100
108
  enabledModules,
101
109
  projectDomain: `${defaultAnswers.projectName}.lndo.site`,
102
110
  salts: Generator.generateSalts(),
@@ -106,10 +114,12 @@ export async function runPrompts({ defaults = false } = {}) {
106
114
  const answers = await inquirer.prompt(allPrompts);
107
115
 
108
116
  const enabledModules = Object.keys(modulePromptDefs).filter(
109
- (mod) => answers[`enable_${mod}`] === true
117
+ (mod) => answers[`enable_${mod.replace(/-/g, '_')}`] === true
110
118
  );
111
119
  return {
112
120
  ...answers,
121
+ phpVersion: '8.4',
122
+ nodeVersion: '24',
113
123
  enabledModules,
114
124
  projectDomain: `${answers.projectName}.lndo.site`,
115
125
  salts: Generator.generateSalts(),
package/lib/utils/git.js CHANGED
@@ -23,16 +23,45 @@ export function getGitUserDefaults() {
23
23
  }
24
24
 
25
25
  /**
26
- * Clona codigodoleo/superpowers-sage e copia seu conteúdo para <targetDir>/.claude
26
+ * Dirs que cada destino copia do repositório superpowers-sage.
27
+ * A subpasta de destino dentro do projeto é TARGET_CONFIG[key].dest.
28
+ */
29
+ const TARGET_CONFIG = {
30
+ claude: {
31
+ dest: '.claude/plugins/superpowers-sage',
32
+ dirs: ['.claude-plugin', 'skills', 'agents', 'hooks'],
33
+ label: 'Claude Code (.claude/)',
34
+ },
35
+ cursor: {
36
+ dest: '.cursor/plugins/local/superpowers-sage',
37
+ dirs: ['.cursor-plugin', 'skills', 'agents', 'hooks', 'rules'],
38
+ label: 'Cursor (.cursor/)',
39
+ },
40
+ ai: {
41
+ dest: '.ai/superpowers-sage',
42
+ dirs: ['skills', 'agents', 'hooks'],
43
+ label: 'Agnóstico (.ai/)',
44
+ },
45
+ };
46
+
47
+ /**
48
+ * Clona codigodoleo/superpowers-sage e copia o conteúdo para os destinos selecionados.
27
49
  * Requer git instalado no host.
50
+ *
28
51
  * @param {string} targetDir - Caminho absoluto do projeto gerado
52
+ * @param {string[]} targets - Lista de destinos: 'claude' | 'cursor' | 'ai'
29
53
  */
30
- export async function installSuperpowersSage(targetDir) {
54
+ export async function installSuperpowersSage(targetDir, targets = ['claude']) {
31
55
  const repo = 'https://github.com/codigodoleo/superpowers-sage.git';
32
56
  const tmpDir = path.join(os.tmpdir(), `superpowers-sage-${Date.now()}`);
33
- const claudeDir = path.join(targetDir, '.claude');
34
57
 
35
- log(color.blue, '🔌 Installing superpowers-sage...');
58
+ const validTargets = targets.filter((t) => TARGET_CONFIG[t]);
59
+ if (validTargets.length === 0) {
60
+ log(color.orange, '[skills] Nenhum destino válido informado. Pulando.');
61
+ return;
62
+ }
63
+
64
+ log(color.blue, `🔌 Clonando superpowers-sage para: ${validTargets.join(', ')}...`);
36
65
 
37
66
  const result = spawnSync('git', ['clone', '--depth=1', repo, tmpDir], {
38
67
  stdio: 'inherit',
@@ -41,19 +70,27 @@ export async function installSuperpowersSage(targetDir) {
41
70
  if (result.status !== 0) {
42
71
  log(
43
72
  color.orange,
44
- '⚠️ Could not clone superpowers-sage (no network or git unavailable). Skipping.'
73
+ '⚠️ Não foi possível clonar superpowers-sage (sem rede ou git indisponível). Pulando.'
45
74
  );
46
75
  return;
47
76
  }
48
77
 
49
- // Copia o conteúdo completo do clone (exceto .git) para .claude do projeto
50
- await fs.ensureDir(claudeDir);
51
- await fs.copy(tmpDir, claudeDir, {
52
- overwrite: true,
53
- filter: (src) => !src.includes(`${path.sep}.git`),
54
- });
78
+ for (const key of validTargets) {
79
+ const cfg = TARGET_CONFIG[key];
80
+ const destBase = path.join(targetDir, cfg.dest);
55
81
 
56
- await fs.remove(tmpDir);
82
+ await fs.ensureDir(destBase);
83
+
84
+ for (const dir of cfg.dirs) {
85
+ const srcDir = path.join(tmpDir, dir);
86
+ if (!(await fs.pathExists(srcDir))) continue;
87
+
88
+ const destDir = path.join(destBase, dir);
89
+ await fs.copy(srcDir, destDir, { overwrite: true });
90
+ }
57
91
 
58
- log(color.green, '✅ superpowers-sage installed at .claude/');
92
+ log(color.green, `✅ superpowers-sage instalado em ${cfg.dest}`);
93
+ }
94
+
95
+ await fs.remove(tmpDir);
59
96
  }
@@ -0,0 +1,71 @@
1
+ import path from 'path';
2
+
3
+ import fs from 'fs-extra';
4
+
5
+ import { log, color } from '../../lib/utils/logger.js';
6
+
7
+ export async function setupModule(context, generator) {
8
+ const themeName = context.blank_theme_name || `${context.projectName}-theme`;
9
+ const themeDir = `content/themes/${themeName}`;
10
+ const absoluteThemeDir = path.join(generator.cwd, themeDir);
11
+
12
+ log(color.blue, `[blank-theme] 📂 Scaffolding blank theme: ${themeDir}`);
13
+
14
+ // 1. Ensure directories exist
15
+ await fs.ensureDir(path.join(absoluteThemeDir, 'src', 'js'));
16
+ await fs.ensureDir(path.join(absoluteThemeDir, 'src', 'scss'));
17
+ await fs.ensureDir(path.join(absoluteThemeDir, 'php'));
18
+
19
+ // 2. Generate static files
20
+ await generator.generateFile(
21
+ 'style.css',
22
+ { ...context, blank_theme_name: themeName },
23
+ 'blank-theme',
24
+ `${themeDir}/style.css`
25
+ );
26
+ await generator.generateFile(
27
+ 'functions.php',
28
+ { ...context, blank_theme_name: themeName },
29
+ 'blank-theme',
30
+ `${themeDir}/functions.php`
31
+ );
32
+ await generator.generateFile(
33
+ 'vite.config.js',
34
+ { ...context, blank_theme_name: themeName },
35
+ 'blank-theme',
36
+ `${themeDir}/vite.config.js`
37
+ );
38
+ await generator.generateFile(
39
+ 'package.json',
40
+ { ...context, blank_theme_name: themeName },
41
+ 'blank-theme',
42
+ `${themeDir}/package.json`
43
+ );
44
+
45
+ // 3. src/ scaffolds
46
+ await fs.ensureFile(path.join(absoluteThemeDir, 'src', 'js', 'app.js'));
47
+ await fs.ensureFile(path.join(absoluteThemeDir, 'src', 'scss', 'app.scss'));
48
+
49
+ // 4. Lint configs co-located in theme — always installed (IDE/editor support)
50
+ log(color.blue, '[blank-theme] 🔧 Installing lint configurations...');
51
+ await generator.copyFile('lint/.prettierrc.json', 'blank-theme', `${themeDir}/.prettierrc.json`);
52
+ if (context.blank_theme_lint_eslint !== false) {
53
+ await generator.copyFile(
54
+ 'lint/eslint.config.mjs',
55
+ 'blank-theme',
56
+ `${themeDir}/eslint.config.mjs`
57
+ );
58
+ }
59
+ if (context.blank_theme_lint_pint !== false) {
60
+ await generator.copyFile('lint/pint.json', 'blank-theme', `${themeDir}/pint.json`);
61
+ }
62
+ if (context.blank_theme_lint_stylelint !== false) {
63
+ await generator.copyFile(
64
+ 'lint/.stylelintrc.json',
65
+ 'blank-theme',
66
+ `${themeDir}/.stylelintrc.json`
67
+ );
68
+ }
69
+
70
+ log(color.green, `[blank-theme] ✅ Blank theme scaffolded: ${themeDir}`);
71
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true,
4
+ "trailingComma": "es5",
5
+ "printWidth": 100,
6
+ "tabWidth": 2,
7
+ "overrides": [
8
+ { "files": "*.blade.php", "options": { "parser": "html" } },
9
+ { "files": "*.vue", "options": { "parser": "vue" } },
10
+ { "files": "*.scss", "options": { "singleQuote": false } }
11
+ ]
12
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": ["stylelint-config-standard-scss"],
3
+ "rules": {
4
+ "color-hex-length": "short",
5
+ "string-quotes": "double"
6
+ }
7
+ }
@@ -5,17 +5,11 @@ import globals from 'globals';
5
5
 
6
6
  export default [
7
7
  {
8
- ignores: [
9
- '**/node_modules/**',
10
- '**/vendor/**',
11
- '**/wp/**',
12
- '**/public/build/**',
13
- '**/dist/**',
14
- 'eslint.config.mjs',
15
- ],
8
+ ignores: ['node_modules/**', 'vendor/**', 'dist/**'],
16
9
  },
17
10
  js.configs.recommended,
18
11
  {
12
+ files: ['src/js/**/*.{js,mjs,ts}'],
19
13
  languageOptions: {
20
14
  ecmaVersion: 2022,
21
15
  sourceType: 'module',
@@ -0,0 +1,11 @@
1
+ {
2
+ "preset": "psr12",
3
+ "rules": {
4
+ "array_syntax": {
5
+ "syntax": "short"
6
+ },
7
+ "binary_operator_spaces": {
8
+ "default": "single_space"
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,36 @@
1
+ export default [
2
+ {
3
+ type: 'confirm',
4
+ name: 'enable_blank_theme',
5
+ message: 'Enable Blank Theme scaffold (Vite + SCSS, no Sage)?',
6
+ default: false,
7
+ },
8
+ {
9
+ type: 'input',
10
+ name: 'blank_theme_name',
11
+ message: 'Blank theme slug (used as directory name):',
12
+ default: (answers) => `${answers.projectName || 'wordpress'}-theme`,
13
+ when: (answers) => answers.enable_blank_theme,
14
+ },
15
+ {
16
+ type: 'confirm',
17
+ name: 'blank_theme_lint_eslint',
18
+ message: 'Blank Theme: enable ESLint for JS/TS?',
19
+ default: true,
20
+ when: (answers) => answers.enable_blank_theme && answers.enable_lint,
21
+ },
22
+ {
23
+ type: 'confirm',
24
+ name: 'blank_theme_lint_stylelint',
25
+ message: 'Blank Theme: enable Stylelint for CSS/SCSS?',
26
+ default: true,
27
+ when: (answers) => answers.enable_blank_theme && answers.enable_lint,
28
+ },
29
+ {
30
+ type: 'confirm',
31
+ name: 'blank_theme_lint_pint',
32
+ message: 'Blank Theme: enable Pint for PHP?',
33
+ default: true,
34
+ when: (answers) => answers.enable_blank_theme && answers.enable_lint,
35
+ },
36
+ ];
@@ -0,0 +1,26 @@
1
+ tooling:
2
+ bt-dev:
3
+ service: build
4
+ description: Run Vite dev server for blank theme
5
+ cmd: npm run dev --prefix /app/content/themes/{{blank_theme_name}}
6
+ bt-build:
7
+ service: build
8
+ description: Build blank theme assets
9
+ cmd: npm run build --prefix /app/content/themes/{{blank_theme_name}}
10
+ {{#if enable_lint}}
11
+ bt-eslint:
12
+ service: build
13
+ description: Lint JS/TS in blank theme
14
+ cmd: npx eslint --fix src/js/ --config /app/content/themes/{{blank_theme_name}}/eslint.config.mjs
15
+ dir: /app/content/themes/{{blank_theme_name}}
16
+ bt-stylelint:
17
+ service: build
18
+ description: Lint CSS/SCSS in blank theme
19
+ cmd: npx stylelint --fix "src/scss/**/*.scss" --config /app/content/themes/{{blank_theme_name}}/.stylelintrc.json
20
+ dir: /app/content/themes/{{blank_theme_name}}
21
+ bt-pint:
22
+ service: appserver
23
+ description: Lint PHP in blank theme
24
+ dir: /app/content/themes/{{blank_theme_name}}
25
+ cmd: ./vendor/bin/pint
26
+ {{/if}}
@@ -0,0 +1,46 @@
1
+ <?php
2
+ /**
3
+ * Theme functions and definitions.
4
+ *
5
+ * @package {{blank_theme_name}}
6
+ */
7
+
8
+ if (!defined('ABSPATH')) {
9
+ exit;
10
+ }
11
+
12
+ define('{{blank_theme_name}}_VERSION', '{{projectVersion}}');
13
+ define('{{blank_theme_name}}_DIR', get_template_directory());
14
+ define('{{blank_theme_name}}_URI', get_template_directory_uri());
15
+
16
+ /**
17
+ * Enqueue Vite-compiled assets.
18
+ */
19
+ add_action('wp_enqueue_scripts', function () {
20
+ $manifest = {{blank_theme_name}}_DIR . '/dist/.vite/manifest.json';
21
+
22
+ if (!file_exists($manifest)) {
23
+ return;
24
+ }
25
+
26
+ $data = json_decode(file_get_contents($manifest), true);
27
+
28
+ if (isset($data['src/js/app.js']['file'])) {
29
+ wp_enqueue_script(
30
+ '{{blank_theme_name}}-app',
31
+ {{blank_theme_name}}_URI . '/dist/' . $data['src/js/app.js']['file'],
32
+ [],
33
+ null,
34
+ true
35
+ );
36
+ }
37
+
38
+ if (isset($data['src/scss/app.scss']['file'])) {
39
+ wp_enqueue_style(
40
+ '{{blank_theme_name}}-app',
41
+ {{blank_theme_name}}_URI . '/dist/' . $data['src/scss/app.scss']['file'],
42
+ [],
43
+ null
44
+ );
45
+ }
46
+ });
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "{{blank_theme_name}}",
3
+ "version": "{{projectVersion}}",
4
+ "description": "{{projectDescription}}",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "lint": "eslint src/js --ext .js,.ts",
10
+ "lint:css": "stylelint \"src/scss/**/*.scss\""
11
+ },
12
+ "devDependencies": {
13
+ "vite": "^6.0.0",
14
+ "sass-embedded": "^1.77.0"
15
+ }
16
+ }
@@ -0,0 +1,7 @@
1
+ /*
2
+ Theme Name: {{blank_theme_name}}
3
+ Description: {{projectDescription}}
4
+ Version: {{projectVersion}}
5
+ Author: {{author}}
6
+ Text Domain: {{blank_theme_name}}
7
+ */
@@ -0,0 +1,23 @@
1
+ import { defineConfig } from 'vite';
2
+
3
+ export default defineConfig({
4
+ base: '/app/content/themes/{{blank_theme_name}}/dist/',
5
+ build: {
6
+ outDir: 'dist',
7
+ rollupOptions: {
8
+ input: {
9
+ app: 'src/js/app.js',
10
+ styles: 'src/scss/app.scss',
11
+ },
12
+ },
13
+ manifest: true,
14
+ emptyOutDir: true,
15
+ },
16
+ css: {
17
+ preprocessorOptions: {
18
+ scss: {
19
+ api: 'modern-compiler',
20
+ },
21
+ },
22
+ },
23
+ });