@loopress/cli 0.32.2 → 0.34.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 +549 -179
- package/dist/commands/acf/list.d.ts +1 -1
- package/dist/commands/acf/list.js +3 -4
- package/dist/commands/acf/pull.d.ts +1 -1
- package/dist/commands/acf/pull.js +9 -6
- package/dist/commands/acf/push.d.ts +1 -1
- package/dist/commands/acf/push.js +5 -4
- package/dist/commands/api/publish.js +2 -1
- package/dist/commands/app/pull.js +2 -1
- package/dist/commands/app/push.js +7 -5
- package/dist/commands/app/{remove.d.ts → rm.d.ts} +1 -0
- package/dist/commands/app/{remove.js → rm.js} +2 -1
- package/dist/commands/block/diff.d.ts +2 -0
- package/dist/commands/block/diff.js +5 -0
- package/dist/commands/block/list.d.ts +8 -0
- package/dist/commands/block/list.js +23 -0
- package/dist/commands/block/pull.d.ts +21 -0
- package/dist/commands/block/pull.js +67 -0
- package/dist/commands/block/push.d.ts +20 -0
- package/dist/commands/block/push.js +53 -0
- package/dist/commands/block/rm.d.ts +23 -0
- package/dist/commands/block/rm.js +69 -0
- package/dist/commands/block/rollback.d.ts +2 -0
- package/dist/commands/block/rollback.js +5 -0
- package/dist/commands/composer/init.js +1 -0
- package/dist/commands/composer/push.js +5 -4
- package/dist/commands/content/copy.d.ts +3 -0
- package/dist/commands/content/copy.js +79 -2
- package/dist/commands/dev.d.ts +2 -2
- package/dist/commands/dev.js +13 -18
- package/dist/commands/diff.d.ts +6 -3
- package/dist/commands/diff.js +63 -13
- package/dist/commands/doctor.d.ts +4 -2
- package/dist/commands/doctor.js +49 -25
- package/dist/commands/form/pull.js +7 -5
- package/dist/commands/form/push.js +4 -3
- package/dist/commands/init.js +4 -3
- package/dist/commands/menu/push.js +8 -8
- package/dist/commands/option/list.js +5 -2
- package/dist/commands/option/push.js +3 -1
- package/dist/commands/option/{remove.d.ts → rm.d.ts} +1 -0
- package/dist/commands/option/{remove.js → rm.js} +2 -1
- package/dist/commands/page/push.js +3 -1
- package/dist/commands/plugin/add.d.ts +1 -0
- package/dist/commands/plugin/add.js +50 -9
- package/dist/commands/plugin/audit.d.ts +4 -4
- package/dist/commands/plugin/audit.js +35 -15
- package/dist/commands/plugin/pull.js +3 -2
- package/dist/commands/plugin/push.js +41 -6
- package/dist/commands/plugin/status.js +3 -1
- package/dist/commands/project/config.js +7 -7
- package/dist/commands/project/pull.js +3 -1
- package/dist/commands/project/push.js +6 -4
- package/dist/commands/project/{remove.d.ts → rm.d.ts} +1 -0
- package/dist/commands/project/{remove.js → rm.js} +3 -2
- package/dist/commands/project/switch.js +6 -4
- package/dist/commands/promote.d.ts +1 -0
- package/dist/commands/promote.js +15 -6
- package/dist/commands/pull.d.ts +7 -0
- package/dist/commands/pull.js +67 -18
- package/dist/commands/push.d.ts +11 -1
- package/dist/commands/push.js +157 -48
- package/dist/commands/snippet/publish.js +9 -5
- package/dist/commands/snippet/push.d.ts +1 -1
- package/dist/commands/snippet/push.js +15 -12
- package/dist/commands/status.js +1 -0
- package/dist/commands/theme/activate.d.ts +21 -0
- package/dist/commands/theme/activate.js +44 -0
- package/dist/commands/theme/pull.js +2 -1
- package/dist/commands/theme/push.js +7 -3
- package/dist/commands/theme/style/diff.js +1 -1
- package/dist/commands/theme/style/push.js +8 -3
- package/dist/commands/theme/style/rollback.js +1 -1
- package/dist/commands/theme/template/push.d.ts +2 -0
- package/dist/commands/theme/template/push.js +66 -18
- package/dist/commands/validate.js +3 -0
- package/dist/config/auth.manager.d.ts +10 -7
- package/dist/config/auth.manager.js +5 -1
- package/dist/config/json-file.d.ts +1 -1
- package/dist/config/project-config.manager.d.ts +2 -2
- package/dist/config/project-config.manager.js +13 -21
- package/dist/lib/api-client.js +16 -21
- package/dist/lib/app-build.js +6 -2
- package/dist/lib/app-manifest.js +6 -6
- package/dist/lib/base.d.ts +8 -1
- package/dist/lib/base.js +43 -9
- package/dist/lib/bootstrap-full-install.js +8 -6
- package/dist/lib/capabilities.d.ts +44 -0
- package/dist/lib/capabilities.js +85 -0
- package/dist/lib/declared-plugins.d.ts +9 -0
- package/dist/lib/declared-plugins.js +21 -0
- package/dist/lib/declared-type-command.js +7 -2
- package/dist/lib/dev-watch.d.ts +1 -1
- package/dist/lib/dev-watch.js +13 -15
- package/dist/lib/diff-command.d.ts +19 -3
- package/dist/lib/diff-command.js +132 -14
- package/dist/lib/diff-state.js +2 -3
- package/dist/lib/environment-urls.d.ts +6 -0
- package/dist/lib/environment-urls.js +59 -0
- package/dist/lib/exclude.d.ts +16 -0
- package/dist/lib/exclude.js +102 -0
- package/dist/lib/load-files.js +2 -1
- package/dist/lib/load-snippets.js +39 -43
- package/dist/lib/local-callback-server.js +7 -1
- package/dist/lib/narrow.d.ts +5 -0
- package/dist/lib/narrow.js +26 -0
- package/dist/lib/php-files-command.js +13 -8
- package/dist/lib/plugin-detection.d.ts +0 -1
- package/dist/lib/plugin-detection.js +2 -4
- package/dist/lib/push-command.d.ts +5 -1
- package/dist/lib/push-command.js +48 -7
- package/dist/lib/put-or-create.d.ts +2 -2
- package/dist/lib/readdir-tolerant.js +9 -4
- package/dist/lib/resource-selection.d.ts +9 -0
- package/dist/lib/resource-selection.js +49 -0
- package/dist/lib/resource-state.d.ts +2 -0
- package/dist/lib/resource-state.js +60 -14
- package/dist/lib/rollback-command.d.ts +1 -1
- package/dist/lib/rollback-command.js +4 -3
- package/dist/lib/rollback-materialize.js +29 -9
- package/dist/lib/rotate-app-password.js +1 -0
- package/dist/lib/sentry.js +2 -2
- package/dist/lib/snapshot-store.d.ts +10 -7
- package/dist/lib/snapshot-store.js +21 -5
- package/dist/lib/spinner.js +4 -0
- package/dist/lib/validate-local.d.ts +1 -0
- package/dist/lib/validate-local.js +102 -28
- package/dist/lib/wp-client.d.ts +7 -1
- package/dist/lib/wp-client.js +60 -69
- package/dist/lib/wp-site-diagnostic.js +3 -5
- package/dist/types/global-config.generated.d.ts +1 -1
- package/dist/types/plugin.d.ts +7 -5
- package/dist/types/plugin.js +6 -1
- package/dist/types/project-config.generated.d.ts +24 -5
- package/dist/types/snippet.d.ts +2 -2
- package/dist/types/snippet.js +1 -1
- package/dist/utils/acf-format.js +6 -3
- package/dist/utils/block-format.d.ts +29 -0
- package/dist/utils/block-format.js +109 -0
- package/dist/utils/composer.d.ts +7 -15
- package/dist/utils/composer.js +43 -16
- package/dist/utils/declared-type-format.js +26 -19
- package/dist/utils/form-format.js +2 -2
- package/dist/utils/loopress-config.js +9 -1
- package/dist/utils/menu-format.d.ts +3 -1
- package/dist/utils/menu-format.js +5 -0
- package/dist/utils/option-format.d.ts +2 -2
- package/dist/utils/option-format.js +15 -25
- package/dist/utils/page-format.js +9 -7
- package/dist/utils/php-top-level.js +15 -4
- package/dist/utils/plugin-sync.d.ts +7 -7
- package/dist/utils/plugin-sync.js +11 -11
- package/dist/utils/plugins.js +9 -6
- package/dist/utils/resource-dirs.d.ts +2 -0
- package/dist/utils/resource-dirs.js +13 -5
- package/dist/utils/snippet-format.d.ts +1 -1
- package/dist/utils/snippet-format.js +5 -3
- package/dist/utils/template-format.js +2 -2
- package/dist/utils/theme-css.js +14 -6
- package/dist/utils/theme-styles-format.d.ts +3 -0
- package/dist/utils/theme-styles-format.js +15 -5
- package/dist/utils/version.js +1 -1
- package/oclif.manifest.json +2661 -1235
- package/package.json +45 -20
- package/schemas/global-config.schema.json +1 -1
- package/schemas/project-config.schema.json +32 -5
package/dist/lib/app-build.js
CHANGED
|
@@ -2,6 +2,8 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { isOneOf } from './narrow.js';
|
|
5
7
|
import { withSpinner } from './spinner.js';
|
|
6
8
|
// The package managers `lps app push` may spawn. An allowlist, not the raw `packageManager`
|
|
7
9
|
// field: the name ends up as a process to run.
|
|
@@ -13,6 +15,8 @@ const LOCKFILES = [
|
|
|
13
15
|
['bun.lockb', 'bun'],
|
|
14
16
|
['package-lock.json', 'npm'],
|
|
15
17
|
];
|
|
18
|
+
// Only the fields read here; the rest of package.json is the app's own business.
|
|
19
|
+
const PACKAGE_JSON = z.looseObject({ packageManager: z.string().optional(), scripts: z.record(z.string(), z.string()).optional() });
|
|
16
20
|
// How many trailing lines of build output a failure message carries.
|
|
17
21
|
const OUTPUT_TAIL_LINES = 20;
|
|
18
22
|
// `packageManager` field first (corepack's own source of truth), then the nearest lockfile
|
|
@@ -20,7 +24,7 @@ const OUTPUT_TAIL_LINES = 20;
|
|
|
20
24
|
// then npm.
|
|
21
25
|
export function detectPackageManager(appDir, packageManagerField) {
|
|
22
26
|
const fromField = packageManagerField?.split('@', 1)[0];
|
|
23
|
-
if (PACKAGE_MANAGERS
|
|
27
|
+
if (isOneOf(PACKAGE_MANAGERS, fromField))
|
|
24
28
|
return fromField;
|
|
25
29
|
for (let dir = appDir;; dir = dirname(dir)) {
|
|
26
30
|
const lockfile = LOCKFILES.find(([file]) => existsSync(join(dir, file)));
|
|
@@ -38,7 +42,7 @@ export async function buildApp(appDir, onStart) {
|
|
|
38
42
|
const packageJsonPath = join(appDir, 'package.json');
|
|
39
43
|
if (!existsSync(packageJsonPath))
|
|
40
44
|
return undefined;
|
|
41
|
-
const pkg = JSON.parse(await readFile(packageJsonPath, 'utf8'));
|
|
45
|
+
const pkg = PACKAGE_JSON.parse(JSON.parse(await readFile(packageJsonPath, 'utf8')));
|
|
42
46
|
if (!pkg.scripts?.build)
|
|
43
47
|
return undefined;
|
|
44
48
|
const pm = detectPackageManager(appDir, pkg.packageManager);
|
package/dist/lib/app-manifest.js
CHANGED
|
@@ -44,9 +44,9 @@ export function computeBuildId(files) {
|
|
|
44
44
|
}
|
|
45
45
|
function normalizeAssetUrl(url) {
|
|
46
46
|
let path = url.trim();
|
|
47
|
-
const schemeMatch = /^(?:[a-z]+:)?\/\/[^/]+(\/.*)$/i.exec(path);
|
|
47
|
+
const schemeMatch = /^(?:[a-z]+:)?\/\/[^/]+(\/.*)$/i.exec(path)?.[1];
|
|
48
48
|
if (schemeMatch)
|
|
49
|
-
path = schemeMatch
|
|
49
|
+
path = schemeMatch;
|
|
50
50
|
path = path.replace(/^\.?\//, '');
|
|
51
51
|
return path;
|
|
52
52
|
}
|
|
@@ -57,17 +57,17 @@ export function deriveEntry(indexHtml) {
|
|
|
57
57
|
for (const tag of indexHtml.match(/<script\b[^>]*>/gi) ?? []) {
|
|
58
58
|
if (!/\btype=["']module["']/i.test(tag))
|
|
59
59
|
continue;
|
|
60
|
-
const src = /\bsrc=["']([^"']+)["']/i.exec(tag);
|
|
60
|
+
const src = /\bsrc=["']([^"']+)["']/i.exec(tag)?.[1];
|
|
61
61
|
if (src)
|
|
62
|
-
scripts.push(normalizeAssetUrl(src
|
|
62
|
+
scripts.push(normalizeAssetUrl(src));
|
|
63
63
|
}
|
|
64
64
|
const styles = [];
|
|
65
65
|
for (const tag of indexHtml.match(/<link\b[^>]*>/gi) ?? []) {
|
|
66
66
|
if (!/\brel=["']stylesheet["']/i.test(tag))
|
|
67
67
|
continue;
|
|
68
|
-
const href = /\bhref=["']([^"']+)["']/i.exec(tag);
|
|
68
|
+
const href = /\bhref=["']([^"']+)["']/i.exec(tag)?.[1];
|
|
69
69
|
if (href)
|
|
70
|
-
styles.push(normalizeAssetUrl(href
|
|
70
|
+
styles.push(normalizeAssetUrl(href));
|
|
71
71
|
}
|
|
72
72
|
return { scripts, styles };
|
|
73
73
|
}
|
package/dist/lib/base.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import { type EnvironmentConfig } from '../types/config.js';
|
|
3
3
|
import { type LoopressLocalConfig } from '../utils/loopress-config.js';
|
|
4
|
+
import { type SiteCapabilities } from './capabilities.js';
|
|
4
5
|
import { WpClient } from './wp-client.js';
|
|
6
|
+
export declare function listrRenderer(silent: boolean): 'default' | 'silent' | 'simple';
|
|
5
7
|
export declare abstract class LoopressCommand extends Command {
|
|
6
8
|
static baseFlags: {
|
|
7
9
|
env: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -9,6 +11,10 @@ export declare abstract class LoopressCommand extends Command {
|
|
|
9
11
|
static dryRunFlag: {
|
|
10
12
|
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
13
|
};
|
|
14
|
+
static honorsExclude: boolean;
|
|
15
|
+
static skipUnsupportedFlag: {
|
|
16
|
+
'skip-unsupported': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
};
|
|
12
18
|
static yesFlag: {
|
|
13
19
|
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
20
|
};
|
|
@@ -16,12 +22,14 @@ export declare abstract class LoopressCommand extends Command {
|
|
|
16
22
|
protected localConfig: LoopressLocalConfig;
|
|
17
23
|
protected overwrite: boolean;
|
|
18
24
|
protected productionConfirmed: boolean;
|
|
25
|
+
protected projectEnvironments: EnvironmentConfig[];
|
|
19
26
|
protected projectId: string;
|
|
20
27
|
protected siteConfig: EnvironmentConfig;
|
|
21
28
|
protected yes: boolean;
|
|
22
29
|
private wpClient?;
|
|
23
30
|
protected get rootDir(): string;
|
|
24
31
|
protected get wp(): WpClient;
|
|
32
|
+
protected capabilities(): Promise<SiteCapabilities>;
|
|
25
33
|
init(): Promise<void>;
|
|
26
34
|
protected maybeAutoRotate(): Promise<void>;
|
|
27
35
|
protected pullDirectory<T>(dir: string, items: T[], orphans: string[], options: {
|
|
@@ -42,7 +50,6 @@ export declare abstract class LoopressCommand extends Command {
|
|
|
42
50
|
protected resolveOptionsPath(override?: string): string;
|
|
43
51
|
protected resolveSnippetsPath(override?: string): string;
|
|
44
52
|
protected resolveThemeStylesPath(override?: string): string;
|
|
45
|
-
protected warnRemovedSeoModule(): void;
|
|
46
53
|
private pickEnvironment;
|
|
47
54
|
private resolveEnvironment;
|
|
48
55
|
}
|
package/dist/lib/base.js
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import { confirm } from '@inquirer/prompts';
|
|
2
2
|
import { Command, Flags } from '@oclif/core';
|
|
3
3
|
import { Listr } from 'listr2';
|
|
4
|
-
import { existsSync } from 'node:fs';
|
|
5
4
|
import { mkdir, rm } from 'node:fs/promises';
|
|
6
5
|
import { join } from 'node:path';
|
|
7
6
|
import { configManager } from '../config/project-config.manager.js';
|
|
7
|
+
import {} from '../types/config.js';
|
|
8
8
|
import { readLocalConfig } from '../utils/loopress-config.js';
|
|
9
9
|
import { pluralize } from '../utils/pluralize.js';
|
|
10
10
|
import { resolveResourceDir } from '../utils/resource-dirs.js';
|
|
11
|
+
import { getCapabilities } from './capabilities.js';
|
|
12
|
+
import { activateExclusions } from './exclude.js';
|
|
11
13
|
import { isInteractive } from './interactive.js';
|
|
12
14
|
import { isAppPasswordStale, rotateAppPassword } from './rotate-app-password.js';
|
|
13
15
|
import { withoutSpinner } from './spinner.js';
|
|
14
16
|
import { WpClient } from './wp-client.js';
|
|
17
|
+
// Listr's default renderer wraps every label to the terminal width, so a terminal reporting no
|
|
18
|
+
// width (columns 0 or undefined, e.g. `script` without a size) renders one letter per line. The
|
|
19
|
+
// simple renderer prints plain lines and does not wrap.
|
|
20
|
+
export function listrRenderer(silent) {
|
|
21
|
+
if (silent)
|
|
22
|
+
return 'silent';
|
|
23
|
+
return process.stdout.columns > 0 ? 'default' : 'simple';
|
|
24
|
+
}
|
|
15
25
|
export class LoopressCommand extends Command {
|
|
16
26
|
// On every subclass without opt-in. Without it, commands target the "local" environment, so
|
|
17
27
|
// reaching a remote site is always spelled out in the command itself.
|
|
@@ -23,6 +33,16 @@ export class LoopressCommand extends Command {
|
|
|
23
33
|
static dryRunFlag = {
|
|
24
34
|
'dry-run': Flags.boolean({ char: 'd', description: 'Show what would change without making changes' }),
|
|
25
35
|
};
|
|
36
|
+
// Whether the environment's `exclude` globs in loopress.json apply (lib/exclude.ts): push and
|
|
37
|
+
// diff commands only, never pull.
|
|
38
|
+
static honorsExclude = false;
|
|
39
|
+
// Opt-in on push, pull, diff and promote: a resource the project uses but the site can't hold
|
|
40
|
+
// (see lib/capabilities.ts) is reported skipped, with a final warning, instead of failing the run.
|
|
41
|
+
static skipUnsupportedFlag = {
|
|
42
|
+
'skip-unsupported': Flags.boolean({
|
|
43
|
+
description: "Skip the resources the project uses but this site can't hold (a missing plugin, a Full feature on Light) instead of failing, and warn about each",
|
|
44
|
+
}),
|
|
45
|
+
};
|
|
26
46
|
static yesFlag = {
|
|
27
47
|
yes: Flags.boolean({ char: 'y', description: 'Answer yes to confirmation prompts' }),
|
|
28
48
|
};
|
|
@@ -32,6 +52,8 @@ export class LoopressCommand extends Command {
|
|
|
32
52
|
overwrite = false;
|
|
33
53
|
// Set by PushCommand's hidden --production-confirmed, see there.
|
|
34
54
|
productionConfirmed = false;
|
|
55
|
+
// Every environment of the project, the target included (see environment-urls.ts).
|
|
56
|
+
projectEnvironments = [];
|
|
35
57
|
projectId;
|
|
36
58
|
siteConfig;
|
|
37
59
|
yes = false;
|
|
@@ -49,10 +71,18 @@ export class LoopressCommand extends Command {
|
|
|
49
71
|
}
|
|
50
72
|
return this.wpClient;
|
|
51
73
|
}
|
|
74
|
+
// What the targeted site can do, read once per process (see lib/capabilities.ts).
|
|
75
|
+
async capabilities() {
|
|
76
|
+
return getCapabilities(this.wp, this.siteConfig.name);
|
|
77
|
+
}
|
|
52
78
|
async init() {
|
|
53
79
|
await super.init();
|
|
80
|
+
// oclif types `this.ctor` as the base `typeof Command`, and parse() can't infer flags from a
|
|
81
|
+
// runtime-chosen subclass: both casts state what every LoopressCommand subclass declares.
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- see above
|
|
54
83
|
const { flags } = (await this.parse({
|
|
55
84
|
args: this.ctor.args,
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- see above
|
|
56
86
|
baseFlags: this.ctor.baseFlags,
|
|
57
87
|
enableJsonFlag: this.ctor.enableJsonFlag,
|
|
58
88
|
flags: this.ctor.flags,
|
|
@@ -66,6 +96,13 @@ export class LoopressCommand extends Command {
|
|
|
66
96
|
const resolved = this.resolveEnvironment(flags.env);
|
|
67
97
|
this.siteConfig = resolved.env;
|
|
68
98
|
this.projectId = resolved.projectId;
|
|
99
|
+
activateExclusions({
|
|
100
|
+
config: this.localConfig,
|
|
101
|
+
cwd: process.cwd(),
|
|
102
|
+
envName: this.siteConfig.name,
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- see above
|
|
104
|
+
honors: this.ctor.honorsExclude,
|
|
105
|
+
});
|
|
69
106
|
await this.maybeAutoRotate();
|
|
70
107
|
}
|
|
71
108
|
// Silent, best-effort: a stale app password still works, so a failed rotation attempt
|
|
@@ -119,7 +156,10 @@ export class LoopressCommand extends Command {
|
|
|
119
156
|
task.output = `Pulled: ${options.title(item)}`;
|
|
120
157
|
},
|
|
121
158
|
title: `Pull ${options.title(item)}`,
|
|
122
|
-
})),
|
|
159
|
+
})),
|
|
160
|
+
// Listr types the task wrapper per renderer, a union of renderer names does not fit it.
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Listr ties the task wrapper type to one renderer
|
|
162
|
+
{ renderer: listrRenderer(this.jsonEnabled()) }).run();
|
|
123
163
|
});
|
|
124
164
|
await this.removeOrphanedFiles(dir, orphans, options.orphanReason);
|
|
125
165
|
this.log(options.pulledMessage);
|
|
@@ -178,13 +218,6 @@ export class LoopressCommand extends Command {
|
|
|
178
218
|
resolveThemeStylesPath(override) {
|
|
179
219
|
return resolveResourceDir('themeStyles', this.localConfig, override);
|
|
180
220
|
}
|
|
181
|
-
// `lps seo` was removed: a leftover seo/ folder or seoDir key would otherwise be silently
|
|
182
|
-
// ignored by `lps pull` / `lps push`. Remove once projects have had a release to migrate.
|
|
183
|
-
warnRemovedSeoModule() {
|
|
184
|
-
if (!('seoDir' in this.localConfig) && !existsSync(join(this.rootDir, 'seo')))
|
|
185
|
-
return;
|
|
186
|
-
this.warn('The SEO module was removed: seo/ is no longer pulled or pushed. Track SEO plugin settings with `lps option add rank-math-options-titles` (or `wpseo_titles`), then delete seo/ and any seoDir key from loopress.json.');
|
|
187
|
-
}
|
|
188
221
|
pickEnvironment(project, envName) {
|
|
189
222
|
const env = project.environments[envName];
|
|
190
223
|
if (!env) {
|
|
@@ -202,6 +235,7 @@ export class LoopressCommand extends Command {
|
|
|
202
235
|
if (!project) {
|
|
203
236
|
this.error(`Project "${projectId}" (from loopress.json) not found. Run \`lps project config\` to configure it.`);
|
|
204
237
|
}
|
|
238
|
+
this.projectEnvironments = Object.values(project.environments);
|
|
205
239
|
const envNames = Object.keys(project.environments);
|
|
206
240
|
if (envNames.length === 0) {
|
|
207
241
|
this.error(`Project "${project.name}" has no environments configured. Run \`lps project config\` to add one.`);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { launchLocalBrowser } from './browser-launch.js';
|
|
2
2
|
import { downloadLatestFullZip } from './github-release.js';
|
|
3
|
+
import { errorMessage } from './narrow.js';
|
|
3
4
|
import { createTempAdmin, deleteTempAdmin } from './temp-admin.js';
|
|
5
|
+
import {} from './wp-client.js';
|
|
4
6
|
/**
|
|
5
7
|
* Installs and activates Loopress Full on a site that doesn't have it yet: downloads the
|
|
6
8
|
* latest release zip, creates a temporary admin (the only way to get `install_plugins` from
|
|
@@ -29,7 +31,7 @@ export async function bootstrapLoopressFull(wp, siteUrl, log) {
|
|
|
29
31
|
// default termination runs with the right exit code.
|
|
30
32
|
removeTempAdmin()
|
|
31
33
|
.catch((error) => {
|
|
32
|
-
log(`Could not remove the temporary admin account "${admin.username}"; remove it manually in wp-admin. (${error
|
|
34
|
+
log(`Could not remove the temporary admin account "${admin.username}"; remove it manually in wp-admin. (${errorMessage(error)})`);
|
|
33
35
|
})
|
|
34
36
|
.finally(() => {
|
|
35
37
|
process.removeListener('SIGINT', onSignal);
|
|
@@ -65,12 +67,12 @@ export async function bootstrapLoopressFull(wp, siteUrl, log) {
|
|
|
65
67
|
process.removeListener('SIGTERM', onSignal);
|
|
66
68
|
}
|
|
67
69
|
const manualFallback = `Install it manually: upload ${zipPath} at ${siteUrl}/wp-admin/plugin-install.php?tab=upload`;
|
|
68
|
-
if (installError && cleanupError) {
|
|
69
|
-
throw new Error(`Could not install Loopress Full automatically, and the temporary admin account could not be removed (${cleanupError
|
|
70
|
+
if (installError !== undefined && cleanupError !== undefined) {
|
|
71
|
+
throw new Error(`Could not install Loopress Full automatically, and the temporary admin account could not be removed (${errorMessage(cleanupError)}). ${manualFallback}`, { cause: installError });
|
|
70
72
|
}
|
|
71
|
-
if (cleanupError)
|
|
72
|
-
throw cleanupError;
|
|
73
|
-
if (installError) {
|
|
73
|
+
if (cleanupError !== undefined)
|
|
74
|
+
throw cleanupError instanceof Error ? cleanupError : new Error(errorMessage(cleanupError));
|
|
75
|
+
if (installError !== undefined) {
|
|
74
76
|
throw new Error(`Could not install Loopress Full automatically. ${manualFallback}`, { cause: installError });
|
|
75
77
|
}
|
|
76
78
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type ResourceDirKind } from '../utils/resource-dirs.js';
|
|
2
|
+
import { type WpClient } from './wp-client.js';
|
|
3
|
+
export type SiteCapabilities = {
|
|
4
|
+
edition: 'full' | 'light';
|
|
5
|
+
environmentType: string;
|
|
6
|
+
integrations: {
|
|
7
|
+
acf: boolean;
|
|
8
|
+
form: boolean;
|
|
9
|
+
snippets?: boolean | undefined;
|
|
10
|
+
};
|
|
11
|
+
modules: string[];
|
|
12
|
+
theme: {
|
|
13
|
+
active: string;
|
|
14
|
+
child?: null | undefined | {
|
|
15
|
+
active: boolean;
|
|
16
|
+
exists: boolean;
|
|
17
|
+
stylesheet: string;
|
|
18
|
+
};
|
|
19
|
+
isBlock: boolean;
|
|
20
|
+
template: string;
|
|
21
|
+
};
|
|
22
|
+
version: string;
|
|
23
|
+
};
|
|
24
|
+
export declare const CAPABILITIES_ENDPOINT = "loopress/v1/capabilities";
|
|
25
|
+
export declare const THEME_ACTIVATE_ENDPOINT = "loopress/v1/theme/activate";
|
|
26
|
+
export declare function getCapabilities(wp: WpClient, site: string): Promise<SiteCapabilities>;
|
|
27
|
+
export type MissingIntegration = {
|
|
28
|
+
fix?: string;
|
|
29
|
+
needsFull?: true;
|
|
30
|
+
plugins?: string[];
|
|
31
|
+
reason: string;
|
|
32
|
+
};
|
|
33
|
+
export declare function missingModule(module: string, what: string, { modules }: SiteCapabilities): MissingIntegration | undefined;
|
|
34
|
+
export declare function missingIntegration(kind: ResourceDirKind, capabilities: SiteCapabilities): MissingIntegration | undefined;
|
|
35
|
+
export declare function integrationFix(missing: MissingIntegration, declared: {
|
|
36
|
+
fromComposer: boolean;
|
|
37
|
+
plugins: string[];
|
|
38
|
+
}): string | undefined;
|
|
39
|
+
export type ThemeActivation = {
|
|
40
|
+
active: string;
|
|
41
|
+
changed: boolean;
|
|
42
|
+
previous: string;
|
|
43
|
+
};
|
|
44
|
+
export declare function activateTheme(wp: WpClient, stylesheet: string): Promise<ThemeActivation>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {} from '../utils/resource-dirs.js';
|
|
2
|
+
import { isNotFoundError } from './wp-client.js';
|
|
3
|
+
export const CAPABILITIES_ENDPOINT = 'loopress/v1/capabilities';
|
|
4
|
+
export const THEME_ACTIVATE_ENDPOINT = 'loopress/v1/theme/activate';
|
|
5
|
+
// One request per site and per process: the aggregate commands ask about every resource.
|
|
6
|
+
const cache = new WeakMap();
|
|
7
|
+
// `site` names it in the error, the environment name or its URL.
|
|
8
|
+
export async function getCapabilities(wp, site) {
|
|
9
|
+
let capabilities = cache.get(wp);
|
|
10
|
+
if (!capabilities) {
|
|
11
|
+
const tooOld = `The Loopress plugin on ${site} is too old for this CLI. Update it, then try again.`;
|
|
12
|
+
capabilities = wp
|
|
13
|
+
.get(CAPABILITIES_ENDPOINT)
|
|
14
|
+
.catch((error) => {
|
|
15
|
+
if (isNotFoundError(error))
|
|
16
|
+
throw new Error(tooOld, { cause: error });
|
|
17
|
+
throw error;
|
|
18
|
+
})
|
|
19
|
+
.then((result) => {
|
|
20
|
+
// A plugin from before `modules` answers without it.
|
|
21
|
+
if (!Array.isArray(result.modules))
|
|
22
|
+
throw new Error(tooOld);
|
|
23
|
+
return result;
|
|
24
|
+
});
|
|
25
|
+
cache.set(wp, capabilities);
|
|
26
|
+
}
|
|
27
|
+
return capabilities;
|
|
28
|
+
}
|
|
29
|
+
// `what` needs the `module` feature of Loopress Full.
|
|
30
|
+
export function missingModule(module, what, { modules }) {
|
|
31
|
+
return modules.includes(module) ? undefined : { fix: 'Install Loopress Full', needsFull: true, reason: `${what} need Loopress Full` };
|
|
32
|
+
}
|
|
33
|
+
const needsModule = (module, what) => (capabilities) => missingModule(module, what, capabilities);
|
|
34
|
+
// The kinds of resource a site may be unable to hold. Every other kind needs nothing from it.
|
|
35
|
+
const INTEGRATION_CHECKS = {
|
|
36
|
+
acf: ({ integrations }) => (integrations.acf ? undefined : { plugins: ['advanced-custom-fields'], reason: 'ACF is not active' }),
|
|
37
|
+
api: needsModule('api', 'API routes'),
|
|
38
|
+
block: needsModule('blocks', 'dynamic blocks'),
|
|
39
|
+
blockCss: needsModule('pages', 'templates and parts'),
|
|
40
|
+
css: needsModule('pages', 'templates and parts'),
|
|
41
|
+
form: ({ integrations }) => (integrations.form ? undefined : { plugins: ['wpforms-lite'], reason: 'no supported form plugin is active' }),
|
|
42
|
+
hooks: needsModule('hooks', 'hooks'),
|
|
43
|
+
page: needsModule('pages', 'pages'),
|
|
44
|
+
part: needsModule('pages', 'templates and parts'),
|
|
45
|
+
pattern: needsModule('pages', 'block patterns'),
|
|
46
|
+
snippets(capabilities) {
|
|
47
|
+
const missing = missingModule('snippets', 'snippets', capabilities);
|
|
48
|
+
if (missing)
|
|
49
|
+
return missing;
|
|
50
|
+
return capabilities.integrations.snippets === true
|
|
51
|
+
? undefined
|
|
52
|
+
: { plugins: ['code-snippets', 'insert-headers-and-footers'], reason: 'no supported snippet plugin is active' };
|
|
53
|
+
},
|
|
54
|
+
template: needsModule('pages', 'templates and parts'),
|
|
55
|
+
themeStyles: ({ theme }) => (theme.isBlock ? undefined : { reason: `the active theme "${theme.active}" is a classic theme` }),
|
|
56
|
+
};
|
|
57
|
+
// Why the site can't hold this kind of resource, or undefined when it can (or the kind needs
|
|
58
|
+
// nothing from the site).
|
|
59
|
+
export function missingIntegration(kind, capabilities) {
|
|
60
|
+
return INTEGRATION_CHECKS[kind]?.(capabilities);
|
|
61
|
+
}
|
|
62
|
+
// How to fix a missing integration, given the plugins the project declares (composer.json, or
|
|
63
|
+
// loopress.json without one): declared but inactive needs activating, otherwise declaring.
|
|
64
|
+
export function integrationFix(missing, declared) {
|
|
65
|
+
if (missing.fix)
|
|
66
|
+
return missing.fix;
|
|
67
|
+
if (!missing.plugins)
|
|
68
|
+
return undefined;
|
|
69
|
+
// composer.json records no active state, loopress.json pins it per plugin.
|
|
70
|
+
const activate = (plugin) => declared.fromComposer ? '`lps plugin push --activate`' : `"${plugin}": {"active": true, ...} in loopress.json, then \`lps plugin push\``;
|
|
71
|
+
const plugin = missing.plugins.find((candidate) => declared.plugins.includes(candidate));
|
|
72
|
+
if (plugin)
|
|
73
|
+
return `The project declares ${plugin}, activate it: ${activate(plugin)}`;
|
|
74
|
+
const [first, ...alternatives] = missing.plugins;
|
|
75
|
+
if (first === undefined)
|
|
76
|
+
return undefined;
|
|
77
|
+
const or = alternatives.length > 0 ? ` (or ${alternatives.join(', ')})` : '';
|
|
78
|
+
const where = declared.fromComposer ? `wpackagist-plugin/${first} in composer.json` : `"${first}" in loopress.json plugins`;
|
|
79
|
+
return `Declare ${first}${or} (${where}), then activate it: ${activate(first)}`;
|
|
80
|
+
}
|
|
81
|
+
export async function activateTheme(wp, stylesheet) {
|
|
82
|
+
const activation = await wp.post(THEME_ACTIVATE_ENDPOINT, { stylesheet });
|
|
83
|
+
cache.delete(wp);
|
|
84
|
+
return activation;
|
|
85
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type LoopressLocalConfig } from '../utils/loopress-config.js';
|
|
2
|
+
export declare function declaredPlugins(options: {
|
|
3
|
+
localConfig: LoopressLocalConfig;
|
|
4
|
+
rootDir: string;
|
|
5
|
+
siteName: string;
|
|
6
|
+
}): Promise<{
|
|
7
|
+
fromComposer: boolean;
|
|
8
|
+
plugins: string[];
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { parseComposerJson, wpackagistRequire } from '../utils/composer.js';
|
|
5
|
+
import {} from '../utils/loopress-config.js';
|
|
6
|
+
import { isLocalEnvironment } from './wp-client.js';
|
|
7
|
+
// The wordpress.org plugins the project declares: composer.json's wpackagist plugins when it
|
|
8
|
+
// exists, loopress.json's otherwise. Unreadable or invalid composer.json: none, the commands
|
|
9
|
+
// report it.
|
|
10
|
+
export async function declaredPlugins(options) {
|
|
11
|
+
const path = resolve(process.cwd(), options.rootDir, 'composer.json');
|
|
12
|
+
if (!existsSync(path))
|
|
13
|
+
return { fromComposer: false, plugins: Object.keys(options.localConfig.plugins ?? {}) };
|
|
14
|
+
try {
|
|
15
|
+
const composerJson = parseComposerJson(await readFile(path, 'utf8'));
|
|
16
|
+
return { fromComposer: true, plugins: Object.keys(wpackagistRequire(composerJson, 'plugin', isLocalEnvironment(options.siteName))) };
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return { fromComposer: true, plugins: [] };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -9,6 +9,8 @@ import { LoopressCommand } from './base.js';
|
|
|
9
9
|
import { basenameKey, findOrphanedFiles } from './find-orphaned-files.js';
|
|
10
10
|
import { isInteractive } from './interactive.js';
|
|
11
11
|
import { loadFiles } from './load-files.js';
|
|
12
|
+
import { errorMessage } from './narrow.js';
|
|
13
|
+
import {} from './php-files-command.js';
|
|
12
14
|
import { PushCommand } from './push-command.js';
|
|
13
15
|
import { getResourceStateProvider } from './resource-state.js';
|
|
14
16
|
import { isApplicative404, isNotFoundError } from './wp-client.js';
|
|
@@ -44,6 +46,9 @@ export function declaredPushCommand(spec, description) {
|
|
|
44
46
|
parse: (raw, filePath) => parseDeclaredFile(spec, filePath, raw),
|
|
45
47
|
});
|
|
46
48
|
this.log(`Found ${pluralize(items.length, spec.noun, spec.plural)} to push`);
|
|
49
|
+
// Patterns carry block markup, the other declared types only registration arguments.
|
|
50
|
+
if (spec.cliName === 'pattern')
|
|
51
|
+
this.warnEnvironmentUrls(items.map((item) => ({ id: `${spec.plural}/${item.slug}`, value: item.args })));
|
|
47
52
|
const pushed = [];
|
|
48
53
|
await this.runPushTasks(items, (item) => item.slug, async (item, task) => {
|
|
49
54
|
await this.pushItem(item, task);
|
|
@@ -87,7 +92,7 @@ export function declaredPushCommand(spec, description) {
|
|
|
87
92
|
task.output = `Pushed: ${slug}`;
|
|
88
93
|
}
|
|
89
94
|
catch (error) {
|
|
90
|
-
this.reportTaskFailure(`Failed to push ${slug}: ${error
|
|
95
|
+
this.reportTaskFailure(`Failed to push ${slug}: ${errorMessage(error)}`, error, task);
|
|
91
96
|
}
|
|
92
97
|
}
|
|
93
98
|
}
|
|
@@ -179,7 +184,7 @@ export function declaredRmCommand(spec, description, keptNote) {
|
|
|
179
184
|
await this.wp.delete(itemEndpoint(spec, slug));
|
|
180
185
|
}
|
|
181
186
|
catch (error) {
|
|
182
|
-
const notFound = `${spec.noun
|
|
187
|
+
const notFound = `${spec.noun.charAt(0).toUpperCase()}${spec.noun.slice(1)} not found`;
|
|
183
188
|
if (isApplicative404(error, notFound))
|
|
184
189
|
this.error(`${spec.noun} ${slug} is not managed by Loopress on ${url}.`);
|
|
185
190
|
throw error;
|
package/dist/lib/dev-watch.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type LoopressLocalConfig } from '../utils/loopress-config.js';
|
|
2
|
-
export type ResourceType = 'api' | '
|
|
2
|
+
export type ResourceType = 'api' | 'block' | 'css' | 'hook' | 'page' | 'part' | 'plugin' | 'snippet' | 'template';
|
|
3
3
|
export declare const RESOURCE_TYPES: ResourceType[];
|
|
4
4
|
export type WatchTarget = {
|
|
5
5
|
commandId: string;
|
package/dist/lib/dev-watch.js
CHANGED
|
@@ -1,35 +1,33 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { join, sep } from 'node:path';
|
|
3
|
+
import {} from '../utils/loopress-config.js';
|
|
3
4
|
import { resolveResourceDir } from '../utils/resource-dirs.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// precedence a combined `--only=a,b --skip=b` reads as: watch a, b, then drop b.
|
|
5
|
+
import { selectResources } from './resource-selection.js';
|
|
6
|
+
export const RESOURCE_TYPES = ['snippet', 'template', 'part', 'css', 'block', 'page', 'api', 'hook', 'plugin'];
|
|
7
7
|
export function resolveResourceTypes(only, skip = []) {
|
|
8
|
-
|
|
9
|
-
return base.filter((type) => !skip.includes(type));
|
|
8
|
+
return selectResources(RESOURCE_TYPES, only, skip);
|
|
10
9
|
}
|
|
11
10
|
// One target per requested type that actually has something to watch: a missing directory (no
|
|
12
11
|
// `api/` folder yet) or an empty plugin manifest is left out silently, matching `push`'s own
|
|
13
12
|
// directories which are all optional. `plugins` watches the whole `loopress.json` lockfile since
|
|
14
13
|
// the plugin manifest is a field in it, not its own directory.
|
|
15
14
|
export function buildWatchTargets(types, localConfig, cwd) {
|
|
16
|
-
const rootDir = join(cwd, localConfig.rootDir ?? '.');
|
|
17
15
|
const candidates = {
|
|
18
|
-
api: { commandId: 'api:push', path: join(
|
|
16
|
+
api: { commandId: 'api:push', path: join(cwd, resolveResourceDir('api', localConfig)), type: 'api' },
|
|
19
17
|
// The theme CSS is bundled into the child theme with the templates, by the same push.
|
|
20
|
-
|
|
18
|
+
block: { commandId: 'theme:template:push', path: join(cwd, resolveResourceDir('blockCss', localConfig)), type: 'block' },
|
|
21
19
|
css: { commandId: 'theme:template:push', path: join(cwd, resolveResourceDir('css', localConfig)), type: 'css' },
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
hook: { commandId: 'hook:push', path: join(cwd, resolveResourceDir('hooks', localConfig)), type: 'hook' },
|
|
21
|
+
page: { commandId: 'page:push', path: join(cwd, resolveResourceDir('page', localConfig)), type: 'page' },
|
|
22
|
+
plugin: { commandId: 'plugin:push', path: join(cwd, 'loopress.json'), type: 'plugin' },
|
|
23
|
+
part: { commandId: 'theme:template:push', path: join(cwd, resolveResourceDir('part', localConfig)), type: 'part' },
|
|
24
|
+
template: { commandId: 'theme:template:push', path: join(cwd, resolveResourceDir('template', localConfig)), type: 'template' },
|
|
25
|
+
snippet: { commandId: 'snippet:push', path: join(cwd, resolveResourceDir('snippets', localConfig)), type: 'snippet' },
|
|
28
26
|
};
|
|
29
27
|
return types
|
|
30
28
|
.map((type) => candidates[type])
|
|
31
29
|
.filter((target) => {
|
|
32
|
-
if (target.type === '
|
|
30
|
+
if (target.type === 'plugin')
|
|
33
31
|
return Object.keys(localConfig.plugins ?? {}).length > 0;
|
|
34
32
|
return existsSync(target.path);
|
|
35
33
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LoopressCommand } from './base.js';
|
|
2
|
+
import { type MissingIntegration } from './capabilities.js';
|
|
2
3
|
import { type ResourceState, type StateChange } from './diff-state.js';
|
|
3
4
|
import { type ResourceStateProvider } from './resource-state.js';
|
|
4
5
|
import { WpClient } from './wp-client.js';
|
|
@@ -6,7 +7,10 @@ export type ResourceDiffJson = {
|
|
|
6
7
|
added: string[];
|
|
7
8
|
changed: StateChange[];
|
|
8
9
|
error?: string;
|
|
10
|
+
heldBack?: string[];
|
|
11
|
+
reason?: string;
|
|
9
12
|
removed: string[];
|
|
13
|
+
status?: 'skipped';
|
|
10
14
|
};
|
|
11
15
|
export type DiffJson = {
|
|
12
16
|
drift: boolean;
|
|
@@ -15,13 +19,20 @@ export type DiffJson = {
|
|
|
15
19
|
right: string;
|
|
16
20
|
};
|
|
17
21
|
export type DiffTarget = {
|
|
22
|
+
heldBackFrom?: (() => Promise<ResourceState>) | undefined;
|
|
23
|
+
isHeldBack?: ((id: string) => boolean) | undefined;
|
|
18
24
|
left: () => Promise<ResourceState>;
|
|
19
|
-
optional?: boolean;
|
|
20
25
|
resource: string;
|
|
21
26
|
right: () => Promise<ResourceState>;
|
|
27
|
+
skip?: boolean;
|
|
22
28
|
title: string;
|
|
29
|
+
unsupported?: {
|
|
30
|
+
fix?: string | undefined;
|
|
31
|
+
missing: MissingIntegration;
|
|
32
|
+
reason: string;
|
|
33
|
+
};
|
|
23
34
|
};
|
|
24
|
-
type DiffSides = {
|
|
35
|
+
export type DiffSides = {
|
|
25
36
|
againstWp: undefined | WpClient;
|
|
26
37
|
leftLabel: string;
|
|
27
38
|
rightHeading: string;
|
|
@@ -32,15 +43,19 @@ export declare abstract class DiffCommand extends LoopressCommand {
|
|
|
32
43
|
static againstFlag: {
|
|
33
44
|
against: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
34
45
|
};
|
|
46
|
+
static honorsExclude: boolean;
|
|
35
47
|
protected composerTarget(sides: DiffSides): DiffTarget;
|
|
36
48
|
protected providerTarget(provider: ResourceStateProvider, sides: DiffSides, dirOverride?: string): DiffTarget;
|
|
37
49
|
protected report(targets: DiffTarget[], sides: DiffSides): Promise<DiffJson>;
|
|
50
|
+
protected reportDrift(result: DiffJson): Promise<void>;
|
|
38
51
|
protected resolveSides(against: string | undefined): DiffSides;
|
|
39
52
|
abstract run(): Promise<DiffJson>;
|
|
40
53
|
private computeResource;
|
|
41
54
|
private out;
|
|
42
55
|
private renderFailure;
|
|
56
|
+
private renderHeldBack;
|
|
43
57
|
private renderSection;
|
|
58
|
+
private renderSkipped;
|
|
44
59
|
private wpForEnvironment;
|
|
45
60
|
}
|
|
46
61
|
export declare function resourceDiffCommand(resource: string, options: {
|
|
@@ -48,4 +63,5 @@ export declare function resourceDiffCommand(resource: string, options: {
|
|
|
48
63
|
description: string;
|
|
49
64
|
pathNoun: string;
|
|
50
65
|
}): typeof DiffCommand;
|
|
51
|
-
export
|
|
66
|
+
export declare function excludedFileFor(dir: string, id: string, provider: Pick<ResourceStateProvider, 'fileExtension' | 'fileName'>): boolean;
|
|
67
|
+
export declare function sharedReads(wp: WpClient): WpClient;
|