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 +51 -0
- package/bin/create-finalbuildwp.js +157 -0
- package/package.json +26 -0
- package/templates/README.md +16 -0
- package/templates/composer.json +22 -0
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
|
+
}
|