@loopress/cli 0.10.0 → 0.12.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 +41 -35
- package/dist/commands/init.js +24 -10
- package/dist/commands/project/config.js +1 -1
- package/dist/commands/project/remove.js +3 -2
- package/dist/commands/project/switch.d.ts +1 -2
- package/dist/commands/project/switch.js +28 -34
- package/dist/commands/project/sync.d.ts +13 -0
- package/dist/commands/project/sync.js +220 -0
- package/dist/commands/snippet/list.d.ts +0 -1
- package/dist/commands/snippet/list.js +4 -7
- package/dist/commands/snippet/pull.d.ts +2 -2
- package/dist/commands/snippet/pull.js +44 -14
- package/dist/commands/snippet/push.d.ts +1 -1
- package/dist/commands/snippet/push.js +56 -26
- package/dist/config/project-config.manager.d.ts +6 -1
- package/dist/config/project-config.manager.js +39 -3
- package/dist/help.js +1 -1
- package/dist/lib/api-client.d.ts +14 -0
- package/dist/lib/api-client.js +46 -0
- package/dist/lib/base.d.ts +0 -1
- package/dist/lib/base.js +0 -5
- package/dist/lib/push-command.js +1 -1
- package/dist/lib/wp-client.d.ts +1 -0
- package/dist/lib/wp-client.js +22 -1
- package/dist/types/global-config.generated.d.ts +8 -0
- package/dist/types/project-config.generated.d.ts +0 -4
- package/dist/types/snippet.d.ts +1 -1
- package/dist/utils/{snippet-plugin.d.ts → snippet-format.d.ts} +3 -19
- package/dist/utils/snippet-format.js +57 -0
- package/oclif.manifest.json +124 -139
- package/package.json +6 -1
- package/schemas/global-config.schema.json +8 -0
- package/schemas/project-config.schema.json +0 -6
- package/dist/utils/snippet-plugin-flag.d.ts +0 -3
- package/dist/utils/snippet-plugin-flag.js +0 -8
- package/dist/utils/snippet-plugin.js +0 -232
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export const SNIPPETS_ENDPOINT = 'loopress/v1/snippets';
|
|
2
|
+
export function parseType(raw) {
|
|
3
|
+
const valid = ['css', 'html', 'js', 'php', 'text'];
|
|
4
|
+
const value = String(raw ?? '').toLowerCase();
|
|
5
|
+
return valid.includes(value) ? value : null;
|
|
6
|
+
}
|
|
7
|
+
const VALID_LOCATIONS = new Set(['admin', 'body', 'everywhere', 'footer', 'frontend', 'header', 'once']);
|
|
8
|
+
export function parseLocation(raw) {
|
|
9
|
+
const value = String(raw ?? '').toLowerCase();
|
|
10
|
+
return VALID_LOCATIONS.has(value) ? value : null;
|
|
11
|
+
}
|
|
12
|
+
export function parseInsertMethod(raw) {
|
|
13
|
+
return raw === 'auto' || raw === 'shortcode' ? raw : null;
|
|
14
|
+
}
|
|
15
|
+
// The sensible default placement for a freshly pushed snippet that doesn't specify a location.
|
|
16
|
+
export function defaultLocationForType(type) {
|
|
17
|
+
switch (type) {
|
|
18
|
+
case 'css': {
|
|
19
|
+
return 'header';
|
|
20
|
+
}
|
|
21
|
+
case 'html':
|
|
22
|
+
case 'js':
|
|
23
|
+
case 'text': {
|
|
24
|
+
return 'footer';
|
|
25
|
+
}
|
|
26
|
+
case 'php': {
|
|
27
|
+
return 'everywhere';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function resolvePriority(raw) {
|
|
32
|
+
const value = Number(raw);
|
|
33
|
+
return Number.isFinite(value) ? value : 10;
|
|
34
|
+
}
|
|
35
|
+
// Snippet files on disk keep the <?php opening tag so they're valid, syntax-highlighted PHP files.
|
|
36
|
+
// The WordPress plugin stores just the executable body, so it's stripped before push and restored
|
|
37
|
+
// on pull (see buildSnippetFile in commands/snippet/pull.ts).
|
|
38
|
+
export function stripPhpOpeningTag(code) {
|
|
39
|
+
return code.replace(/^<\?php\s*/i, '');
|
|
40
|
+
}
|
|
41
|
+
// Defensive coercion for whatever JSON the `loopress/v1/snippets` endpoint returns, in case a
|
|
42
|
+
// field is missing or malformed server-side.
|
|
43
|
+
export function normalizeSnippet(data) {
|
|
44
|
+
return {
|
|
45
|
+
active: Boolean(data.active),
|
|
46
|
+
code: String(data.code ?? ''),
|
|
47
|
+
description: String(data.description ?? ''),
|
|
48
|
+
id: Number(data.id),
|
|
49
|
+
insertMethod: parseInsertMethod(data.insertMethod) ?? 'auto',
|
|
50
|
+
location: parseLocation(data.location) ?? defaultLocationForType(parseType(data.type) ?? 'php'),
|
|
51
|
+
name: String(data.name ?? ''),
|
|
52
|
+
priority: resolvePriority(data.priority),
|
|
53
|
+
shortcodeAttributes: Array.isArray(data.shortcodeAttributes) ? data.shortcodeAttributes.map(String) : [],
|
|
54
|
+
tags: Array.isArray(data.tags) ? data.tags : [],
|
|
55
|
+
type: parseType(data.type) ?? 'php',
|
|
56
|
+
};
|
|
57
|
+
}
|
package/oclif.manifest.json
CHANGED
|
@@ -311,109 +311,12 @@
|
|
|
311
311
|
"push.js"
|
|
312
312
|
]
|
|
313
313
|
},
|
|
314
|
-
"project:config": {
|
|
315
|
-
"aliases": [],
|
|
316
|
-
"args": {},
|
|
317
|
-
"description": "Add or update a WordPress project environment",
|
|
318
|
-
"examples": [
|
|
319
|
-
"$ lps project config"
|
|
320
|
-
],
|
|
321
|
-
"flags": {},
|
|
322
|
-
"hasDynamicHelp": false,
|
|
323
|
-
"hiddenAliases": [],
|
|
324
|
-
"id": "project:config",
|
|
325
|
-
"pluginAlias": "@loopress/cli",
|
|
326
|
-
"pluginName": "@loopress/cli",
|
|
327
|
-
"pluginType": "core",
|
|
328
|
-
"strict": true,
|
|
329
|
-
"enableJsonFlag": false,
|
|
330
|
-
"isESM": true,
|
|
331
|
-
"relativePath": [
|
|
332
|
-
"dist",
|
|
333
|
-
"commands",
|
|
334
|
-
"project",
|
|
335
|
-
"config.js"
|
|
336
|
-
]
|
|
337
|
-
},
|
|
338
|
-
"project:list": {
|
|
339
|
-
"aliases": [],
|
|
340
|
-
"args": {},
|
|
341
|
-
"description": "List configured WordPress projects",
|
|
342
|
-
"examples": [
|
|
343
|
-
"$ lps project list"
|
|
344
|
-
],
|
|
345
|
-
"flags": {},
|
|
346
|
-
"hasDynamicHelp": false,
|
|
347
|
-
"hiddenAliases": [],
|
|
348
|
-
"id": "project:list",
|
|
349
|
-
"pluginAlias": "@loopress/cli",
|
|
350
|
-
"pluginName": "@loopress/cli",
|
|
351
|
-
"pluginType": "core",
|
|
352
|
-
"strict": true,
|
|
353
|
-
"enableJsonFlag": false,
|
|
354
|
-
"isESM": true,
|
|
355
|
-
"relativePath": [
|
|
356
|
-
"dist",
|
|
357
|
-
"commands",
|
|
358
|
-
"project",
|
|
359
|
-
"list.js"
|
|
360
|
-
]
|
|
361
|
-
},
|
|
362
|
-
"project:remove": {
|
|
363
|
-
"aliases": [],
|
|
364
|
-
"args": {},
|
|
365
|
-
"description": "Remove one or more WordPress projects or environments",
|
|
366
|
-
"examples": [
|
|
367
|
-
"$ lps project remove"
|
|
368
|
-
],
|
|
369
|
-
"flags": {},
|
|
370
|
-
"hasDynamicHelp": false,
|
|
371
|
-
"hiddenAliases": [],
|
|
372
|
-
"id": "project:remove",
|
|
373
|
-
"pluginAlias": "@loopress/cli",
|
|
374
|
-
"pluginName": "@loopress/cli",
|
|
375
|
-
"pluginType": "core",
|
|
376
|
-
"strict": true,
|
|
377
|
-
"enableJsonFlag": false,
|
|
378
|
-
"isESM": true,
|
|
379
|
-
"relativePath": [
|
|
380
|
-
"dist",
|
|
381
|
-
"commands",
|
|
382
|
-
"project",
|
|
383
|
-
"remove.js"
|
|
384
|
-
]
|
|
385
|
-
},
|
|
386
|
-
"project:switch": {
|
|
387
|
-
"aliases": [],
|
|
388
|
-
"args": {},
|
|
389
|
-
"description": "Switch the active project and environment",
|
|
390
|
-
"examples": [
|
|
391
|
-
"$ lps project switch"
|
|
392
|
-
],
|
|
393
|
-
"flags": {},
|
|
394
|
-
"hasDynamicHelp": false,
|
|
395
|
-
"hiddenAliases": [],
|
|
396
|
-
"id": "project:switch",
|
|
397
|
-
"pluginAlias": "@loopress/cli",
|
|
398
|
-
"pluginName": "@loopress/cli",
|
|
399
|
-
"pluginType": "core",
|
|
400
|
-
"strict": true,
|
|
401
|
-
"enableJsonFlag": false,
|
|
402
|
-
"isESM": true,
|
|
403
|
-
"relativePath": [
|
|
404
|
-
"dist",
|
|
405
|
-
"commands",
|
|
406
|
-
"project",
|
|
407
|
-
"switch.js"
|
|
408
|
-
]
|
|
409
|
-
},
|
|
410
314
|
"snippet:list": {
|
|
411
315
|
"aliases": [],
|
|
412
316
|
"args": {},
|
|
413
317
|
"description": "List snippets from WordPress",
|
|
414
318
|
"examples": [
|
|
415
|
-
"$ lps snippet list"
|
|
416
|
-
"$ lps snippet list --plugin wpcode"
|
|
319
|
+
"$ lps snippet list"
|
|
417
320
|
],
|
|
418
321
|
"flags": {
|
|
419
322
|
"json": {
|
|
@@ -422,18 +325,6 @@
|
|
|
422
325
|
"name": "json",
|
|
423
326
|
"allowNo": false,
|
|
424
327
|
"type": "boolean"
|
|
425
|
-
},
|
|
426
|
-
"plugin": {
|
|
427
|
-
"char": "p",
|
|
428
|
-
"description": "WordPress snippet plugin to target (overrides loopress.json)",
|
|
429
|
-
"name": "plugin",
|
|
430
|
-
"hasDynamicHelp": false,
|
|
431
|
-
"multiple": false,
|
|
432
|
-
"options": [
|
|
433
|
-
"code-snippets",
|
|
434
|
-
"wpcode"
|
|
435
|
-
],
|
|
436
|
-
"type": "option"
|
|
437
328
|
}
|
|
438
329
|
},
|
|
439
330
|
"hasDynamicHelp": false,
|
|
@@ -471,8 +362,7 @@
|
|
|
471
362
|
"description": "Pull snippets from WordPress",
|
|
472
363
|
"examples": [
|
|
473
364
|
"$ lps snippet pull",
|
|
474
|
-
"$ lps snippet pull --path ./snippets"
|
|
475
|
-
"$ lps snippet pull --plugin wpcode"
|
|
365
|
+
"$ lps snippet pull --path ./snippets"
|
|
476
366
|
],
|
|
477
367
|
"flags": {
|
|
478
368
|
"dry-run": {
|
|
@@ -481,18 +371,6 @@
|
|
|
481
371
|
"name": "dry-run",
|
|
482
372
|
"allowNo": false,
|
|
483
373
|
"type": "boolean"
|
|
484
|
-
},
|
|
485
|
-
"plugin": {
|
|
486
|
-
"char": "p",
|
|
487
|
-
"description": "WordPress snippet plugin to target (overrides loopress.json)",
|
|
488
|
-
"name": "plugin",
|
|
489
|
-
"hasDynamicHelp": false,
|
|
490
|
-
"multiple": false,
|
|
491
|
-
"options": [
|
|
492
|
-
"code-snippets",
|
|
493
|
-
"wpcode"
|
|
494
|
-
],
|
|
495
|
-
"type": "option"
|
|
496
374
|
}
|
|
497
375
|
},
|
|
498
376
|
"hasDynamicHelp": false,
|
|
@@ -530,8 +408,7 @@
|
|
|
530
408
|
"description": "Push snippets to WordPress. Local snippet files created or updated remotely are renamed on disk to the `<id>-<slug>` convention.",
|
|
531
409
|
"examples": [
|
|
532
410
|
"$ lps snippet push",
|
|
533
|
-
"$ lps snippet push --path ./snippets"
|
|
534
|
-
"$ lps snippet push --plugin wpcode"
|
|
411
|
+
"$ lps snippet push --path ./snippets"
|
|
535
412
|
],
|
|
536
413
|
"flags": {
|
|
537
414
|
"dry-run": {
|
|
@@ -540,18 +417,6 @@
|
|
|
540
417
|
"name": "dry-run",
|
|
541
418
|
"allowNo": false,
|
|
542
419
|
"type": "boolean"
|
|
543
|
-
},
|
|
544
|
-
"plugin": {
|
|
545
|
-
"char": "p",
|
|
546
|
-
"description": "WordPress snippet plugin to target (overrides loopress.json)",
|
|
547
|
-
"name": "plugin",
|
|
548
|
-
"hasDynamicHelp": false,
|
|
549
|
-
"multiple": false,
|
|
550
|
-
"options": [
|
|
551
|
-
"code-snippets",
|
|
552
|
-
"wpcode"
|
|
553
|
-
],
|
|
554
|
-
"type": "option"
|
|
555
420
|
}
|
|
556
421
|
},
|
|
557
422
|
"hasDynamicHelp": false,
|
|
@@ -568,7 +433,127 @@
|
|
|
568
433
|
"snippet",
|
|
569
434
|
"push.js"
|
|
570
435
|
]
|
|
436
|
+
},
|
|
437
|
+
"project:config": {
|
|
438
|
+
"aliases": [],
|
|
439
|
+
"args": {},
|
|
440
|
+
"description": "Add or update a WordPress project environment",
|
|
441
|
+
"examples": [
|
|
442
|
+
"$ lps project config"
|
|
443
|
+
],
|
|
444
|
+
"flags": {},
|
|
445
|
+
"hasDynamicHelp": false,
|
|
446
|
+
"hiddenAliases": [],
|
|
447
|
+
"id": "project:config",
|
|
448
|
+
"pluginAlias": "@loopress/cli",
|
|
449
|
+
"pluginName": "@loopress/cli",
|
|
450
|
+
"pluginType": "core",
|
|
451
|
+
"strict": true,
|
|
452
|
+
"enableJsonFlag": false,
|
|
453
|
+
"isESM": true,
|
|
454
|
+
"relativePath": [
|
|
455
|
+
"dist",
|
|
456
|
+
"commands",
|
|
457
|
+
"project",
|
|
458
|
+
"config.js"
|
|
459
|
+
]
|
|
460
|
+
},
|
|
461
|
+
"project:list": {
|
|
462
|
+
"aliases": [],
|
|
463
|
+
"args": {},
|
|
464
|
+
"description": "List configured WordPress projects",
|
|
465
|
+
"examples": [
|
|
466
|
+
"$ lps project list"
|
|
467
|
+
],
|
|
468
|
+
"flags": {},
|
|
469
|
+
"hasDynamicHelp": false,
|
|
470
|
+
"hiddenAliases": [],
|
|
471
|
+
"id": "project:list",
|
|
472
|
+
"pluginAlias": "@loopress/cli",
|
|
473
|
+
"pluginName": "@loopress/cli",
|
|
474
|
+
"pluginType": "core",
|
|
475
|
+
"strict": true,
|
|
476
|
+
"enableJsonFlag": false,
|
|
477
|
+
"isESM": true,
|
|
478
|
+
"relativePath": [
|
|
479
|
+
"dist",
|
|
480
|
+
"commands",
|
|
481
|
+
"project",
|
|
482
|
+
"list.js"
|
|
483
|
+
]
|
|
484
|
+
},
|
|
485
|
+
"project:remove": {
|
|
486
|
+
"aliases": [],
|
|
487
|
+
"args": {},
|
|
488
|
+
"description": "Remove one or more WordPress projects or environments",
|
|
489
|
+
"examples": [
|
|
490
|
+
"$ lps project remove"
|
|
491
|
+
],
|
|
492
|
+
"flags": {},
|
|
493
|
+
"hasDynamicHelp": false,
|
|
494
|
+
"hiddenAliases": [],
|
|
495
|
+
"id": "project:remove",
|
|
496
|
+
"pluginAlias": "@loopress/cli",
|
|
497
|
+
"pluginName": "@loopress/cli",
|
|
498
|
+
"pluginType": "core",
|
|
499
|
+
"strict": true,
|
|
500
|
+
"enableJsonFlag": false,
|
|
501
|
+
"isESM": true,
|
|
502
|
+
"relativePath": [
|
|
503
|
+
"dist",
|
|
504
|
+
"commands",
|
|
505
|
+
"project",
|
|
506
|
+
"remove.js"
|
|
507
|
+
]
|
|
508
|
+
},
|
|
509
|
+
"project:switch": {
|
|
510
|
+
"aliases": [],
|
|
511
|
+
"args": {},
|
|
512
|
+
"description": "Switch the active project and environment",
|
|
513
|
+
"examples": [
|
|
514
|
+
"$ lps project switch"
|
|
515
|
+
],
|
|
516
|
+
"flags": {},
|
|
517
|
+
"hasDynamicHelp": false,
|
|
518
|
+
"hiddenAliases": [],
|
|
519
|
+
"id": "project:switch",
|
|
520
|
+
"pluginAlias": "@loopress/cli",
|
|
521
|
+
"pluginName": "@loopress/cli",
|
|
522
|
+
"pluginType": "core",
|
|
523
|
+
"strict": true,
|
|
524
|
+
"enableJsonFlag": false,
|
|
525
|
+
"isESM": true,
|
|
526
|
+
"relativePath": [
|
|
527
|
+
"dist",
|
|
528
|
+
"commands",
|
|
529
|
+
"project",
|
|
530
|
+
"switch.js"
|
|
531
|
+
]
|
|
532
|
+
},
|
|
533
|
+
"project:sync": {
|
|
534
|
+
"aliases": [],
|
|
535
|
+
"args": {},
|
|
536
|
+
"description": "Sync locally configured projects and environments with your Loopress account",
|
|
537
|
+
"examples": [
|
|
538
|
+
"$ lps project sync"
|
|
539
|
+
],
|
|
540
|
+
"flags": {},
|
|
541
|
+
"hasDynamicHelp": false,
|
|
542
|
+
"hiddenAliases": [],
|
|
543
|
+
"id": "project:sync",
|
|
544
|
+
"pluginAlias": "@loopress/cli",
|
|
545
|
+
"pluginName": "@loopress/cli",
|
|
546
|
+
"pluginType": "core",
|
|
547
|
+
"strict": true,
|
|
548
|
+
"enableJsonFlag": false,
|
|
549
|
+
"isESM": true,
|
|
550
|
+
"relativePath": [
|
|
551
|
+
"dist",
|
|
552
|
+
"commands",
|
|
553
|
+
"project",
|
|
554
|
+
"sync.js"
|
|
555
|
+
]
|
|
571
556
|
}
|
|
572
557
|
},
|
|
573
|
-
"version": "0.
|
|
558
|
+
"version": "0.12.0"
|
|
574
559
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loopress/cli",
|
|
3
3
|
"description": "CLI tool for syncing WordPress code snippets, plugins, and Composer dependencies via the REST API",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.12.0",
|
|
5
5
|
"author": "jean-smaug",
|
|
6
6
|
"bin": {
|
|
7
7
|
"loopress": "bin/run.js",
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
"@eslint/compat": "2.1.0",
|
|
27
27
|
"@oclif/prettier-config": "^0.2.1",
|
|
28
28
|
"@oclif/test": "^4.1.20",
|
|
29
|
+
"@stryker-mutator/core": "^9.6.1",
|
|
30
|
+
"@stryker-mutator/vitest-runner": "^9.6.1",
|
|
29
31
|
"@types/node": "26.0.1",
|
|
30
32
|
"@types/write-file-atomic": "^4.0.3",
|
|
31
33
|
"@vitest/coverage-v8": "4.1.9",
|
|
@@ -36,6 +38,7 @@
|
|
|
36
38
|
"oclif": "^4.23.24",
|
|
37
39
|
"prettier": "3.9.4",
|
|
38
40
|
"shx": "^0.4.0",
|
|
41
|
+
"ts-node": "10.9.2",
|
|
39
42
|
"tsx": "4.22.4",
|
|
40
43
|
"typescript": "^6.0.3",
|
|
41
44
|
"vitest": "^4.1.9"
|
|
@@ -111,6 +114,8 @@
|
|
|
111
114
|
"posttest": "pnpm run lint",
|
|
112
115
|
"schema:types": "tsx scripts/generate-config-types.ts",
|
|
113
116
|
"test": "vitest run",
|
|
117
|
+
"pretest:mutation": "pnpm run schema:types",
|
|
118
|
+
"test:mutation": "stryker run",
|
|
114
119
|
"version": "oclif readme && git add README.md",
|
|
115
120
|
"format": "prettier . --write"
|
|
116
121
|
}
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
"type": "string",
|
|
45
45
|
"description": "ISO timestamp of when the project was added."
|
|
46
46
|
},
|
|
47
|
+
"apiProjectId": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"description": "Id of the matching project on the Loopress API, once synced via `lps project sync`."
|
|
50
|
+
},
|
|
47
51
|
"environments": {
|
|
48
52
|
"type": "object",
|
|
49
53
|
"description": "Environments for this project, keyed by environment name.",
|
|
@@ -65,6 +69,10 @@
|
|
|
65
69
|
"type": "string",
|
|
66
70
|
"description": "ISO timestamp of when the environment was added."
|
|
67
71
|
},
|
|
72
|
+
"apiEnvironmentId": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"description": "Id of the matching environment on the Loopress API, once synced via `lps project sync`."
|
|
75
|
+
},
|
|
68
76
|
"name": {
|
|
69
77
|
"type": "string",
|
|
70
78
|
"description": "Environment name (e.g. \"production\", \"staging\")."
|
|
@@ -23,12 +23,6 @@
|
|
|
23
23
|
"type": "string",
|
|
24
24
|
"description": "Project identifier from the global Loopress config. When set, takes precedence over the currently active project in the global config."
|
|
25
25
|
},
|
|
26
|
-
"snippetPlugin": {
|
|
27
|
-
"type": "string",
|
|
28
|
-
"description": "WordPress snippet plugin to use for pull and push commands.",
|
|
29
|
-
"enum": ["wpcode", "code-snippets"],
|
|
30
|
-
"default": "wpcode"
|
|
31
|
-
},
|
|
32
26
|
"plugins": {
|
|
33
27
|
"type": "object",
|
|
34
28
|
"description": "Pinned plugin versions. Keys are WordPress.org plugin slugs, values are version constraints.",
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
export function parseType(raw) {
|
|
2
|
-
const valid = ['css', 'html', 'js', 'php', 'text'];
|
|
3
|
-
const value = String(raw ?? '').toLowerCase();
|
|
4
|
-
return valid.includes(value) ? value : null;
|
|
5
|
-
}
|
|
6
|
-
const VALID_LOCATIONS = new Set(['admin', 'body', 'everywhere', 'footer', 'frontend', 'header', 'once']);
|
|
7
|
-
export function parseLocation(raw) {
|
|
8
|
-
const value = String(raw ?? '').toLowerCase();
|
|
9
|
-
return VALID_LOCATIONS.has(value) ? value : null;
|
|
10
|
-
}
|
|
11
|
-
export function parseInsertMethod(raw) {
|
|
12
|
-
return raw === 'auto' || raw === 'shortcode' ? raw : null;
|
|
13
|
-
}
|
|
14
|
-
// The sensible default placement for a freshly pushed snippet that doesn't specify a location.
|
|
15
|
-
export function defaultLocationForType(type) {
|
|
16
|
-
switch (type) {
|
|
17
|
-
case 'css': {
|
|
18
|
-
return 'header';
|
|
19
|
-
}
|
|
20
|
-
case 'html':
|
|
21
|
-
case 'js':
|
|
22
|
-
case 'text': {
|
|
23
|
-
return 'footer';
|
|
24
|
-
}
|
|
25
|
-
case 'php': {
|
|
26
|
-
return 'everywhere';
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function inferTypeFromCode(code) {
|
|
31
|
-
const firstLine = code.trimStart().split('\n')[0].trimStart();
|
|
32
|
-
if (firstLine.startsWith('<?'))
|
|
33
|
-
return 'php';
|
|
34
|
-
if (firstLine.startsWith('<'))
|
|
35
|
-
return 'html';
|
|
36
|
-
return 'php';
|
|
37
|
-
}
|
|
38
|
-
function resolveType(raw, code) {
|
|
39
|
-
return parseType(raw) ?? inferTypeFromCode(code);
|
|
40
|
-
}
|
|
41
|
-
function resolvePriority(raw) {
|
|
42
|
-
const value = Number(raw);
|
|
43
|
-
return Number.isFinite(value) ? value : 10;
|
|
44
|
-
}
|
|
45
|
-
// Snippet files on disk keep the <?php opening tag so they're valid, syntax-highlighted PHP files.
|
|
46
|
-
// Both plugins store just the executable body, so it's stripped before push and restored on pull
|
|
47
|
-
// (see buildSnippetFile in commands/snippet/pull.ts).
|
|
48
|
-
function stripPhpOpeningTag(code) {
|
|
49
|
-
return code.replace(/^<\?php\s*/i, '');
|
|
50
|
-
}
|
|
51
|
-
// The real Code Snippets plugin has no independent "type" field: its REST API only has `scope`,
|
|
52
|
-
// and the snippet type is derived from it (see WPCode_Snippet::get_type_from_scope() upstream).
|
|
53
|
-
// Sending a `type` key (as this adapter used to) is silently ignored by that plugin.
|
|
54
|
-
const CODE_SNIPPETS_SCOPE_TO_LOCATION = {
|
|
55
|
-
admin: 'admin',
|
|
56
|
-
'admin-css': 'admin',
|
|
57
|
-
content: 'everywhere',
|
|
58
|
-
'footer-content': 'footer',
|
|
59
|
-
'front-end': 'frontend',
|
|
60
|
-
global: 'everywhere',
|
|
61
|
-
'head-content': 'header',
|
|
62
|
-
'single-use': 'once',
|
|
63
|
-
'site-css': 'frontend',
|
|
64
|
-
'site-footer-js': 'footer',
|
|
65
|
-
'site-head-js': 'header',
|
|
66
|
-
};
|
|
67
|
-
function typeFromScope(scope) {
|
|
68
|
-
if (scope.endsWith('-css'))
|
|
69
|
-
return 'css';
|
|
70
|
-
if (scope.endsWith('-js'))
|
|
71
|
-
return 'js';
|
|
72
|
-
if (scope.endsWith('content'))
|
|
73
|
-
return 'html';
|
|
74
|
-
return 'php';
|
|
75
|
-
}
|
|
76
|
-
function scopeFromTypeAndLocation(type, location) {
|
|
77
|
-
switch (type) {
|
|
78
|
-
case 'css': {
|
|
79
|
-
if (location === 'frontend')
|
|
80
|
-
return 'site-css';
|
|
81
|
-
if (location === 'admin')
|
|
82
|
-
return 'admin-css';
|
|
83
|
-
throw new Error(`Code Snippets does not support the "${location}" location for CSS snippets. Use one of: frontend, admin.`);
|
|
84
|
-
}
|
|
85
|
-
case 'html': {
|
|
86
|
-
if (location === 'header')
|
|
87
|
-
return 'head-content';
|
|
88
|
-
if (location === 'footer')
|
|
89
|
-
return 'footer-content';
|
|
90
|
-
if (location === 'everywhere')
|
|
91
|
-
return 'content';
|
|
92
|
-
throw new Error(`Code Snippets does not support the "${location}" location for HTML snippets. Use one of: header, footer, everywhere.`);
|
|
93
|
-
}
|
|
94
|
-
case 'js': {
|
|
95
|
-
if (location === 'header')
|
|
96
|
-
return 'site-head-js';
|
|
97
|
-
if (location === 'footer')
|
|
98
|
-
return 'site-footer-js';
|
|
99
|
-
throw new Error(`Code Snippets does not support the "${location}" location for JS snippets. Use one of: header, footer.`);
|
|
100
|
-
}
|
|
101
|
-
case 'php': {
|
|
102
|
-
if (location === 'everywhere')
|
|
103
|
-
return 'global';
|
|
104
|
-
if (location === 'frontend')
|
|
105
|
-
return 'front-end';
|
|
106
|
-
if (location === 'admin')
|
|
107
|
-
return 'admin';
|
|
108
|
-
if (location === 'once')
|
|
109
|
-
return 'single-use';
|
|
110
|
-
throw new Error(`Code Snippets does not support the "${location}" location for PHP snippets. Use one of: everywhere, frontend, admin, once.`);
|
|
111
|
-
}
|
|
112
|
-
case 'text': {
|
|
113
|
-
throw new Error('Code Snippets has no "text" snippet type. Change the sidecar "type", or push this snippet to "wpcode" instead.');
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
class CodeSnippetsPlugin {
|
|
118
|
-
endpointPath() {
|
|
119
|
-
return 'code-snippets/v1/snippets';
|
|
120
|
-
}
|
|
121
|
-
fromRemote(data) {
|
|
122
|
-
const scope = String(data.scope ?? 'global');
|
|
123
|
-
const type = typeFromScope(scope);
|
|
124
|
-
return {
|
|
125
|
-
active: Boolean(data.active),
|
|
126
|
-
code: String(data.code ?? ''),
|
|
127
|
-
description: String(data.desc ?? ''),
|
|
128
|
-
id: Number(data.id),
|
|
129
|
-
insertMethod: 'auto',
|
|
130
|
-
location: CODE_SNIPPETS_SCOPE_TO_LOCATION[scope] ?? defaultLocationForType(type),
|
|
131
|
-
name: String(data.name ?? ''),
|
|
132
|
-
priority: resolvePriority(data.priority),
|
|
133
|
-
shortcodeAttributes: [],
|
|
134
|
-
tags: Array.isArray(data.tags) ? data.tags : [],
|
|
135
|
-
type,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
toPayload({ active, code, location, name, path, priority, tags, type }) {
|
|
139
|
-
return {
|
|
140
|
-
active,
|
|
141
|
-
code: stripPhpOpeningTag(code),
|
|
142
|
-
desc: `Imported from ${path}`,
|
|
143
|
-
name,
|
|
144
|
-
priority,
|
|
145
|
-
scope: scopeFromTypeAndLocation(type, location),
|
|
146
|
-
tags,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
// WPCode taxonomy term slugs (see wpcode_register_taxonomies() and the auto-insert location
|
|
151
|
-
// classes upstream). 'everywhere' | 'frontend_only' | 'admin_only' | 'on_demand' only apply to
|
|
152
|
-
// PHP snippets; 'site_wide_header' | 'site_wide_body' | 'site_wide_footer' apply to any type.
|
|
153
|
-
const WPCODE_PHP_ONLY_LOCATIONS = {
|
|
154
|
-
admin: 'admin_only',
|
|
155
|
-
everywhere: 'everywhere',
|
|
156
|
-
frontend: 'frontend_only',
|
|
157
|
-
once: 'on_demand',
|
|
158
|
-
};
|
|
159
|
-
const WPCODE_UNIVERSAL_LOCATIONS = {
|
|
160
|
-
body: 'site_wide_body',
|
|
161
|
-
footer: 'site_wide_footer',
|
|
162
|
-
header: 'site_wide_header',
|
|
163
|
-
};
|
|
164
|
-
const WPCODE_LOCATION_TO_CANONICAL = {
|
|
165
|
-
'admin_only': 'admin',
|
|
166
|
-
everywhere: 'everywhere',
|
|
167
|
-
'frontend_only': 'frontend',
|
|
168
|
-
'on_demand': 'once',
|
|
169
|
-
'site_wide_body': 'body',
|
|
170
|
-
'site_wide_footer': 'footer',
|
|
171
|
-
'site_wide_header': 'header',
|
|
172
|
-
};
|
|
173
|
-
function wpcodeLocationTerm(type, location) {
|
|
174
|
-
const universal = WPCODE_UNIVERSAL_LOCATIONS[location];
|
|
175
|
-
if (universal)
|
|
176
|
-
return universal;
|
|
177
|
-
if (type === 'php') {
|
|
178
|
-
const phpOnly = WPCODE_PHP_ONLY_LOCATIONS[location];
|
|
179
|
-
if (phpOnly)
|
|
180
|
-
return phpOnly;
|
|
181
|
-
}
|
|
182
|
-
const allowed = type === 'php' ? 'header, body, footer, everywhere, frontend, admin, once' : 'header, body, footer';
|
|
183
|
-
throw new Error(`WPCode does not support the "${location}" location for ${type} snippets. Use one of: ${allowed}.`);
|
|
184
|
-
}
|
|
185
|
-
class WPCodePlugin {
|
|
186
|
-
endpointPath() {
|
|
187
|
-
return 'loopress/v1/wpcode/snippets';
|
|
188
|
-
}
|
|
189
|
-
fromRemote(data) {
|
|
190
|
-
const type = resolveType(data.type, String(data.code ?? ''));
|
|
191
|
-
return {
|
|
192
|
-
active: Boolean(data.active),
|
|
193
|
-
code: String(data.code ?? ''),
|
|
194
|
-
description: String(data.note ?? ''),
|
|
195
|
-
id: Number(data.id),
|
|
196
|
-
insertMethod: data.insert_method === 'shortcode' ? 'shortcode' : 'auto',
|
|
197
|
-
location: WPCODE_LOCATION_TO_CANONICAL[String(data.location)] ?? defaultLocationForType(type),
|
|
198
|
-
name: String(data.title ?? ''),
|
|
199
|
-
priority: resolvePriority(data.priority),
|
|
200
|
-
shortcodeAttributes: Array.isArray(data.shortcode_attributes) ? data.shortcode_attributes.map(String) : [],
|
|
201
|
-
tags: Array.isArray(data.tags) ? data.tags : [],
|
|
202
|
-
type,
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
toPayload({ active, code, insertMethod, location, name, path, priority, shortcodeAttributes, tags, type, }) {
|
|
206
|
-
const placement = insertMethod === 'shortcode'
|
|
207
|
-
? { 'shortcode_attributes': shortcodeAttributes }
|
|
208
|
-
: { location: wpcodeLocationTerm(type, location) };
|
|
209
|
-
return {
|
|
210
|
-
active,
|
|
211
|
-
code: stripPhpOpeningTag(code),
|
|
212
|
-
note: `Imported from ${path}`,
|
|
213
|
-
priority,
|
|
214
|
-
tags,
|
|
215
|
-
title: name,
|
|
216
|
-
type,
|
|
217
|
-
...placement,
|
|
218
|
-
'insert_method': insertMethod,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
export function getSnippetPlugin(name) {
|
|
223
|
-
switch (name) {
|
|
224
|
-
case 'wpcode': {
|
|
225
|
-
return new WPCodePlugin();
|
|
226
|
-
}
|
|
227
|
-
case 'code-snippets':
|
|
228
|
-
default: {
|
|
229
|
-
return new CodeSnippetsPlugin();
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|