create-finalbuildwp 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.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # create-finalbuildwp
2
+
3
+ CLI для создания папки проекта (**например `my-site`**) с `composer.json`, который тянет **`finalbuildwp/wordpress-build`** из вашего Git. После `composer install` ядро WordPress оказывается **в корне этой папки** (как в основном репозитории сборки), а не в `web/`.
4
+
5
+ ## Требования
6
+
7
+ - **Node.js** 18+
8
+ - **Composer** и **PHP** (для шага установки зависимостей)
9
+ - Доступ на **чтение** к закрытому репозиторию с пакетом (коллаборатор); на машине — тот же SSH/HTTPS, что для `git clone`
10
+
11
+ ## Использование
12
+
13
+ Опубликованный пакет (после `npm publish`):
14
+
15
+ ```bash
16
+ npm create finalbuildwp@latest
17
+ ```
18
+
19
+ или:
20
+
21
+ ```bash
22
+ npx create-finalbuildwp@latest my-site
23
+ ```
24
+
25
+ Локально из клона **finalBuildWP**:
26
+
27
+ ```bash
28
+ cd create-finalbuildwp
29
+ npm install
30
+ node bin/create-finalbuildwp.js my-site
31
+ ```
32
+
33
+ Флаги:
34
+
35
+ - **`--force`** — использовать непустую папку (файлы шаблона перезапишут одноимённые)
36
+ - **`--skip-composer`** — только сгенерировать файлы, без `composer install`
37
+ - **`COMPOSER_BIN`** — путь к исполняемому файлу Composer, если не в `PATH`
38
+
39
+ ## Публикация на npm
40
+
41
+ ```bash
42
+ cd create-finalbuildwp
43
+ npm login
44
+ npm publish --access public
45
+ ```
46
+
47
+ При **scoped** имени (`@org/create-finalbuildwp`) добавьте в `package.json` `"publishConfig": { "access": "public" }` при необходимости.
48
+
49
+ ## Связь с репозиторием сборки
50
+
51
+ Шаблон повторяет смысл [consumer-example/composer.json](../consumer-example/composer.json): корневой проект, `repositories` → VCS, `require` → `finalbuildwp/wordpress-build`. Document root веб-сервера = папка проекта.
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { spawnSync } = require('child_process');
7
+ const prompts = require('prompts');
8
+
9
+ function parseArgs(argv) {
10
+ const force = argv.includes('--force');
11
+ const skipComposer = argv.includes('--skip-composer');
12
+ const pos = argv.filter((a) => !a.startsWith('-'));
13
+ return {
14
+ force,
15
+ skipComposer,
16
+ projectDir: pos[0] || null,
17
+ };
18
+ }
19
+
20
+ function isSafeRelativeDir(name) {
21
+ if (!name || typeof name !== 'string') return false;
22
+ const trimmed = name.trim();
23
+ if (!trimmed) return false;
24
+ if (trimmed.includes('..')) return false;
25
+ if (path.isAbsolute(trimmed)) return false;
26
+ // одна или несколько сегментов без странных символов
27
+ return /^[\w./\\-]+$/.test(trimmed);
28
+ }
29
+
30
+ async function main() {
31
+ const cwd = process.cwd();
32
+ const args = parseArgs(process.argv.slice(2));
33
+
34
+ let projectDir = args.projectDir;
35
+ if (!projectDir) {
36
+ const r = await prompts(
37
+ {
38
+ type: 'text',
39
+ name: 'projectDir',
40
+ message: 'Папка проекта (document root = её корень; WordPress будет здесь же)',
41
+ initial: 'my-site',
42
+ validate: (v) =>
43
+ isSafeRelativeDir(v) ? true : 'Укажите относительное имя папки (без ..)',
44
+ },
45
+ { onCancel: () => process.exit(1) }
46
+ );
47
+ if (!r.projectDir) process.exit(1);
48
+ projectDir = r.projectDir.trim();
49
+ }
50
+
51
+ if (!isSafeRelativeDir(projectDir)) {
52
+ console.error('Некорректное имя папки проекта.');
53
+ process.exit(1);
54
+ }
55
+
56
+ const target = path.resolve(cwd, projectDir);
57
+
58
+ const r2 = await prompts(
59
+ [
60
+ {
61
+ type: 'text',
62
+ name: 'composerName',
63
+ message: 'Имя пакета в composer.json (vendor/name)',
64
+ initial: 'my-company/my-wordpress-site',
65
+ validate: (v) =>
66
+ /^[a-z0-9]([_.-]?[a-z0-9]+)*\/[a-z0-9]([_.-]?[a-z0-9]+)*$/i.test(v)
67
+ ? true
68
+ : 'Формат: vendor/package (строчные буквы, цифры, - . _)',
69
+ },
70
+ {
71
+ type: 'text',
72
+ name: 'vcsUrl',
73
+ message: 'URL Git с репозиторием finalbuildwp/wordpress-build (приватный или публичный)',
74
+ validate: (v) => {
75
+ const s = (v || '').trim();
76
+ if (!s) return 'Укажите URL';
77
+ if (s.startsWith('git@')) return true;
78
+ if (/^https?:\/\//i.test(s)) return true;
79
+ return 'Нужен https://... или git@...';
80
+ },
81
+ },
82
+ {
83
+ type: 'text',
84
+ name: 'packageConstraint',
85
+ message: 'Версия пакета finalbuildwp/wordpress-build',
86
+ initial: 'dev-main',
87
+ validate: (v) => ((v || '').trim() ? true : 'Укажите ограничение версии'),
88
+ },
89
+ ],
90
+ { onCancel: () => process.exit(1) }
91
+ );
92
+
93
+ if (!r2.composerName || !r2.vcsUrl || !r2.packageConstraint) {
94
+ process.exit(1);
95
+ }
96
+
97
+ const vcsUrl = r2.vcsUrl.trim();
98
+ const composerName = r2.composerName.trim();
99
+ const packageConstraint = r2.packageConstraint.trim();
100
+
101
+ if (fs.existsSync(target)) {
102
+ const entries = fs.readdirSync(target);
103
+ if (entries.length > 0 && !args.force) {
104
+ console.error(
105
+ `Папка «${projectDir}» не пуста. Удалите файлы, укажите другое имя или запустите с флагом --force.`
106
+ );
107
+ process.exit(1);
108
+ }
109
+ } else {
110
+ fs.mkdirSync(target, { recursive: true });
111
+ }
112
+
113
+ const tplRoot = path.join(__dirname, '..', 'templates');
114
+ const files = ['composer.json', '.gitignore', 'README.md'];
115
+
116
+ for (const file of files) {
117
+ const src = path.join(tplRoot, file);
118
+ let content = fs.readFileSync(src, 'utf8');
119
+ if (file === 'composer.json') {
120
+ content = content
121
+ .replace(/\{\{VCS_URL\}\}/g, vcsUrl)
122
+ .replace(/\{\{COMPOSER_NAME\}\}/g, composerName)
123
+ .replace(/\{\{PACKAGE_CONSTRAINT\}\}/g, packageConstraint);
124
+ }
125
+ fs.writeFileSync(path.join(target, file), content, 'utf8');
126
+ }
127
+
128
+ console.log(`\nСоздан проект: ${target}\n`);
129
+
130
+ if (args.skipComposer) {
131
+ console.log('Пропуск composer (--skip-composer). Выполните в папке проекта: composer install\n');
132
+ return;
133
+ }
134
+
135
+ const composerBin = process.env.COMPOSER_BIN || 'composer';
136
+ const isWin = process.platform === 'win32';
137
+ const result = spawnSync(composerBin, ['install'], {
138
+ cwd: target,
139
+ stdio: 'inherit',
140
+ shell: isWin,
141
+ env: process.env,
142
+ });
143
+
144
+ if (result.error) {
145
+ console.error(
146
+ '\nНе удалось запустить Composer. Установите его и выполните в папке проекта:\n composer install\n'
147
+ );
148
+ process.exit(1);
149
+ }
150
+
151
+ process.exit(result.status === null ? 1 : result.status);
152
+ }
153
+
154
+ main().catch((e) => {
155
+ console.error(e);
156
+ process.exit(1);
157
+ });
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "create-finalbuildwp",
3
+ "version": "0.1.0",
4
+ "description": "Создаёт проект my-site с finalbuildwp/wordpress-build: ядро WordPress в корне папки (не web/)",
5
+ "bin": {
6
+ "create-finalbuildwp": "bin/create-finalbuildwp.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "templates"
11
+ ],
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
15
+ "keywords": [
16
+ "wordpress",
17
+ "finalbuildwp",
18
+ "composer",
19
+ "scaffold",
20
+ "create-project"
21
+ ],
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "prompts": "^2.4.2"
25
+ }
26
+ }
@@ -0,0 +1,16 @@
1
+ # WordPress site
2
+
3
+ Сгенерировано **create-finalbuildwp**. Ядро WordPress после `composer install` окажется **в корне этой папки** (`index.php`, `wp-admin/`, …), рядом с `composer.json` и `vendor/`.
4
+
5
+ ## Дальше
6
+
7
+ 1. У вас должен быть **доступ на чтение** к закрытому репозиторию с пакетом `finalbuildwp/wordpress-build` (вас добавили коллаборатором). Используйте тот же Git (SSH или HTTPS), что и для `git clone`.
8
+ 2. Если ещё не выполняли: в корне проекта **`composer install`**.
9
+ 3. Скопируйте **`.env.example` → `.env`**, настройте БД и URL (**`WP_HOME`** / **`WP_SITEURL`**).
10
+ 4. **Document root** веб-сервера = **эта папка** (корень проекта). Закройте доступ к **`/vendor/`** и **`.env`**.
11
+
12
+ ## Обновление сборки
13
+
14
+ ```bash
15
+ composer update finalbuildwp/wordpress-build
16
+ ```
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "{{COMPOSER_NAME}}",
3
+ "description": "WordPress site (finalbuildwp/wordpress-build, core in project root)",
4
+ "type": "project",
5
+ "repositories": [
6
+ {
7
+ "type": "vcs",
8
+ "url": "{{VCS_URL}}"
9
+ }
10
+ ],
11
+ "require": {
12
+ "php": ">=7.4",
13
+ "finalbuildwp/wordpress-build": "{{PACKAGE_CONSTRAINT}}"
14
+ },
15
+ "config": {
16
+ "allow-plugins": {
17
+ "johnpbloch/wordpress-core-installer": true,
18
+ "composer/installers": true,
19
+ "finalbuildwp/wordpress-build": true
20
+ }
21
+ }
22
+ }