create-vimp-game 0.2.1 → 0.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-vimp-game",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "Scaffolder for VIMP game plugins",
5
5
  "keywords": [
6
6
  "vimp",
package/src/cli.js CHANGED
@@ -26,6 +26,8 @@ export const USAGE = `Usage: create-vimp-game <directory> [options]
26
26
  --title <title> human-readable title
27
27
  --package <name> npm package name (default: @vimp-games/<id>)
28
28
  --author <name>
29
+ --repository <url> project repository (URL or user/repo) — the engine links
30
+ to it from the game's entry form
29
31
  --yes, -y accept all defaults, no prompts
30
32
  --force allow a non-empty target directory
31
33
  --no-git skip \`git init\`
@@ -39,6 +41,7 @@ const VALUE_OPTIONS = new Map([
39
41
  ['--title', 'title'],
40
42
  ['--package', 'packageName'],
41
43
  ['--author', 'author'],
44
+ ['--repository', 'repository'],
42
45
  ['--engine-path', 'enginePath'],
43
46
  ['--core-path', 'corePath'],
44
47
  ]);
@@ -114,6 +117,9 @@ export function buildDefaults(args) {
114
117
  title: args.title ?? defaultTitle(id),
115
118
  packageName: args.packageName ?? defaultPackageName(id),
116
119
  author: args.author ?? '',
120
+ // репозиторий не выводится из author: тот — имя человека, а не логин на
121
+ // хостинге, и угаданный URL уехал бы битой ссылкой в футер к игрокам
122
+ repository: args.repository ?? '',
117
123
  };
118
124
  }
119
125
 
@@ -200,6 +206,7 @@ export async function main(argv) {
200
206
  force: args.force === true,
201
207
  enginePath: args.enginePath,
202
208
  corePath: args.corePath,
209
+ repository: answers.repository,
203
210
  });
204
211
 
205
212
  if (args.git) {
package/src/generator.js CHANGED
@@ -144,6 +144,53 @@ async function applyEnginePath(targetDir, enginePath) {
144
144
  await writeFile(file, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
145
145
  }
146
146
 
147
+ // Приводит введённое человеком к голому https-адресу проекта, из которого
148
+ // собираются оба поля. Разбираются все формы, которые реально вводят руками:
149
+ // шорткат `user/repo`, строка из кнопки "Code → HTTPS" (хвост `.git`),
150
+ // scp-форма `git@host:a/b`, `git+`/`ssh://`/`git://`, `#readme` и слэш.
151
+ //
152
+ // Движковый resolveProjectUrl сюда не импортируется: у пакета нулевые
153
+ // рантайм-зависимости (см. prompts.js), а vimp-engine ему даже не
154
+ // зависимость. Формы держать согласованными с packages/engine/src/lib/
155
+ // packageLink.js — их читает та же ссылка в футере.
156
+ function normalizeRepositoryUrl(raw) {
157
+ let url = raw.trim().replace(/^git\+/, '');
158
+
159
+ if (/^[\w.-]+\/[\w.-]+$/.test(url)) {
160
+ return `https://github.com/${url}`;
161
+ }
162
+
163
+ // scp-форма git@host:user/repo — двоеточие здесь разделитель, не порт
164
+ const scp = /^git@([^:/]+):(.+)$/.exec(url);
165
+
166
+ if (scp) {
167
+ url = `https://${scp[1]}/${scp[2]}`;
168
+ }
169
+
170
+ return url
171
+ .replace(/^ssh:\/\/(git@)?/, 'https://')
172
+ .replace(/^git:\/\//, 'https://')
173
+ .replace(/#readme$/, '')
174
+ .replace(/\.git(?=$|[#?])/, '')
175
+ .replace(/\/+$/, '');
176
+ }
177
+
178
+ // Адрес проекта в package.json: движок берёт из него ссылку в футере формы
179
+ // входа (docs/en/client.md), а правило контракта A7 предупреждает, когда поля
180
+ // нет. Пишется только по явно заданному значению — угаданный URL уехал бы
181
+ // битой ссылкой к игрокам.
182
+ async function applyRepository(targetDir, repository) {
183
+ const url = normalizeRepositoryUrl(repository);
184
+
185
+ const file = path.join(targetDir, 'package.json');
186
+ const manifest = JSON.parse(await readFile(file, 'utf8'));
187
+
188
+ manifest.repository = { type: 'git', url: `git+${url}.git` };
189
+ manifest.homepage = `${url}#readme`;
190
+
191
+ await writeFile(file, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
192
+ }
193
+
147
194
  async function applyCorePath(targetDir, corePath) {
148
195
  const file = path.join(targetDir, 'Cargo.toml');
149
196
  const patch =
@@ -171,6 +218,7 @@ export async function generate({
171
218
  force = false,
172
219
  enginePath,
173
220
  corePath,
221
+ repository = '',
174
222
  }) {
175
223
  await ensureTargetDir(targetDir, { force });
176
224
 
@@ -185,6 +233,13 @@ export async function generate({
185
233
  await applyEnginePath(targetDir, enginePath);
186
234
  }
187
235
 
236
+ if (
237
+ (repository ?? '').trim() !== '' &&
238
+ (await exists(path.join(targetDir, 'package.json')))
239
+ ) {
240
+ await applyRepository(targetDir, repository);
241
+ }
242
+
188
243
  if (
189
244
  corePath !== undefined &&
190
245
  (await exists(path.join(targetDir, 'Cargo.toml')))
package/src/prompts.js CHANGED
@@ -29,7 +29,7 @@ export async function ask(question, fallback = '') {
29
29
  }
30
30
 
31
31
  // переспрос на месте: без него невалидный id всплывал бы TokenError-ом уже
32
- // после всех пяти вопросов, и вводить пришлось бы заново
32
+ // после всех шести вопросов, и вводить пришлось бы заново
33
33
  export async function askValid(question, fallback, isValid, hint) {
34
34
  for (;;) {
35
35
  const answer = await ask(question, fallback);
@@ -72,6 +72,9 @@ export async function askAnswers(defaults, { interactive = true, derive } = {})
72
72
  'expected an npm package name, e.g. @vimp-games/space-arena',
73
73
  ),
74
74
  author: await ask('Author', defaults.author),
75
+ // пустой ответ допустим: поля repository тогда не будет вовсе, о чём
76
+ // скажет правило контракта A7
77
+ repository: await ask('Repository (URL or user/repo)', defaults.repository),
75
78
  };
76
79
  } finally {
77
80
  closePrompts();
@@ -1,4 +1,4 @@
1
1
  {
2
- "engine": "0.17.0",
2
+ "engine": "0.18.1",
3
3
  "core": "0.8.4"
4
4
  }