@loopress/cli 0.29.0 → 0.30.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 +1301 -345
- package/dist/commands/cpt/diff.js +5 -0
- package/dist/commands/cpt/list.d.ts +2 -0
- package/dist/commands/cpt/list.js +4 -0
- package/dist/commands/cpt/pull.d.ts +2 -0
- package/dist/commands/cpt/pull.js +4 -0
- package/dist/commands/cpt/push.d.ts +2 -0
- package/dist/commands/cpt/push.js +4 -0
- package/dist/commands/cpt/rm.d.ts +2 -0
- package/dist/commands/cpt/rm.js +3 -0
- package/dist/commands/cpt/rollback.js +5 -0
- package/dist/commands/dev.js +14 -4
- package/dist/commands/diff.js +1 -1
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +30 -2
- package/dist/commands/meta/diff.d.ts +2 -0
- package/dist/commands/meta/diff.js +5 -0
- package/dist/commands/meta/list.d.ts +19 -0
- package/dist/commands/meta/list.js +21 -0
- package/dist/commands/meta/pull.d.ts +2 -0
- package/dist/commands/meta/pull.js +4 -0
- package/dist/commands/meta/push.d.ts +2 -0
- package/dist/commands/meta/push.js +4 -0
- package/dist/commands/meta/rm.d.ts +2 -0
- package/dist/commands/meta/rm.js +3 -0
- package/dist/commands/meta/rollback.d.ts +2 -0
- package/dist/commands/meta/rollback.js +5 -0
- package/dist/commands/option/list.js +2 -8
- package/dist/commands/pattern/diff.d.ts +2 -0
- package/dist/commands/pattern/diff.js +5 -0
- package/dist/commands/pattern/list.d.ts +2 -0
- package/dist/commands/pattern/list.js +3 -0
- package/dist/commands/pattern/pull.d.ts +2 -0
- package/dist/commands/pattern/pull.js +4 -0
- package/dist/commands/pattern/push.d.ts +2 -0
- package/dist/commands/pattern/push.js +4 -0
- package/dist/commands/pattern/rm.d.ts +2 -0
- package/dist/commands/pattern/rm.js +3 -0
- package/dist/commands/pattern/rollback.d.ts +2 -0
- package/dist/commands/pattern/rollback.js +5 -0
- package/dist/commands/plugin/audit.js +2 -1
- package/dist/commands/project/config.d.ts +1 -0
- package/dist/commands/project/config.js +20 -1
- package/dist/commands/project/list.d.ts +1 -1
- package/dist/commands/project/list.js +6 -6
- package/dist/commands/project/pull.js +11 -8
- package/dist/commands/project/push.js +23 -18
- package/dist/commands/project/remove.js +1 -1
- package/dist/commands/project/switch.d.ts +0 -1
- package/dist/commands/project/switch.js +16 -36
- package/dist/commands/pull.js +7 -2
- package/dist/commands/push.js +9 -2
- package/dist/commands/role/diff.d.ts +2 -0
- package/dist/commands/role/diff.js +5 -0
- package/dist/commands/role/list.d.ts +2 -0
- package/dist/commands/role/list.js +4 -0
- package/dist/commands/role/pull.d.ts +2 -0
- package/dist/commands/role/pull.js +4 -0
- package/dist/commands/role/push.d.ts +2 -0
- package/dist/commands/role/push.js +4 -0
- package/dist/commands/role/rm.d.ts +2 -0
- package/dist/commands/role/rm.js +3 -0
- package/dist/commands/role/rollback.d.ts +2 -0
- package/dist/commands/role/rollback.js +5 -0
- package/dist/commands/status.d.ts +2 -3
- package/dist/commands/status.js +21 -53
- package/dist/commands/taxonomy/diff.d.ts +2 -0
- package/dist/commands/taxonomy/diff.js +5 -0
- package/dist/commands/taxonomy/list.d.ts +2 -0
- package/dist/commands/taxonomy/list.js +5 -0
- package/dist/commands/taxonomy/pull.d.ts +2 -0
- package/dist/commands/taxonomy/pull.js +4 -0
- package/dist/commands/taxonomy/push.d.ts +2 -0
- package/dist/commands/taxonomy/push.js +4 -0
- package/dist/commands/taxonomy/rm.d.ts +2 -0
- package/dist/commands/taxonomy/rm.js +3 -0
- package/dist/commands/taxonomy/rollback.d.ts +2 -0
- package/dist/commands/taxonomy/rollback.js +5 -0
- package/dist/commands/theme/template/push.d.ts +2 -0
- package/dist/commands/theme/template/push.js +26 -5
- package/dist/config/project-config.manager.d.ts +3 -3
- package/dist/config/project-config.manager.js +22 -31
- package/dist/hooks/finally.js +11 -3
- package/dist/lib/api-client.js +2 -1
- package/dist/lib/app-build.js +3 -1
- package/dist/lib/base.d.ts +1 -2
- package/dist/lib/base.js +30 -38
- package/dist/lib/declared-type-command.d.ts +35 -0
- package/dist/lib/declared-type-command.js +192 -0
- package/dist/lib/dev-watch.d.ts +1 -1
- package/dist/lib/dev-watch.js +7 -3
- package/dist/lib/github-release.js +4 -0
- package/dist/lib/local-callback-server.js +3 -1
- package/dist/lib/push-command.d.ts +1 -0
- package/dist/lib/push-command.js +31 -10
- package/dist/lib/put-or-create.js +3 -4
- package/dist/lib/resource-state.js +33 -80
- package/dist/lib/rollback-materialize.js +13 -24
- package/dist/lib/spinner.d.ts +2 -0
- package/dist/lib/spinner.js +72 -0
- package/dist/lib/temp-admin.d.ts +10 -1
- package/dist/lib/temp-admin.js +27 -2
- package/dist/lib/validate-local.js +31 -9
- package/dist/lib/wp-client.d.ts +1 -0
- package/dist/lib/wp-client.js +7 -1
- package/dist/lib/wp-site-diagnostic.js +3 -2
- package/dist/types/global-config.generated.d.ts +1 -5
- package/dist/types/project-config.generated.d.ts +28 -4
- package/dist/utils/composer.js +2 -1
- package/dist/utils/declared-type-format.d.ts +52 -0
- package/dist/utils/declared-type-format.js +233 -0
- package/dist/utils/resource-dirs.d.ts +9 -3
- package/dist/utils/resource-dirs.js +16 -4
- package/dist/utils/template-format.d.ts +1 -0
- package/dist/utils/theme-css.d.ts +19 -0
- package/dist/utils/theme-css.js +100 -0
- package/oclif.manifest.json +3012 -1133
- package/package.json +28 -15
- package/schemas/global-config.schema.json +2 -6
- package/schemas/project-config.schema.json +37 -7
- package/dist/commands/seo/diff.js +0 -5
- package/dist/commands/seo/list.d.ts +0 -18
- package/dist/commands/seo/list.js +0 -58
- package/dist/commands/seo/pull.d.ts +0 -24
- package/dist/commands/seo/pull.js +0 -89
- package/dist/commands/seo/push.d.ts +0 -29
- package/dist/commands/seo/push.js +0 -178
- package/dist/commands/seo/rollback.js +0 -5
- package/dist/utils/seo-format.d.ts +0 -29
- package/dist/utils/seo-format.js +0 -18
- /package/dist/commands/{seo → cpt}/diff.d.ts +0 -0
- /package/dist/commands/{seo → cpt}/rollback.d.ts +0 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { select
|
|
2
|
-
import { Command
|
|
1
|
+
import { select } from '@inquirer/prompts';
|
|
2
|
+
import { Command } from '@oclif/core';
|
|
3
3
|
import { configManager } from '../../config/project-config.manager.js';
|
|
4
|
-
const c = ux.colorize;
|
|
5
4
|
export default class Switch extends Command {
|
|
6
|
-
static description = 'Switch the active project
|
|
5
|
+
static description = 'Switch the active project (pass --env to target a non-local environment)';
|
|
7
6
|
static examples = ['$ lps project switch'];
|
|
8
7
|
async run() {
|
|
9
8
|
await this.parse(Switch);
|
|
@@ -11,37 +10,18 @@ export default class Switch extends Command {
|
|
|
11
10
|
if (projects.length === 0) {
|
|
12
11
|
this.error('No projects configured. Run `lps project config` first.');
|
|
13
12
|
}
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
const { env, project } = entries[0];
|
|
28
|
-
return { envName: env.name, projectId: project.id, projectName: project.name };
|
|
29
|
-
}
|
|
30
|
-
const choices = groups.flatMap(({ envs, project }) => [
|
|
31
|
-
new Separator(c(project.isCurrent ? 'green' : 'dim', `─── ${project.name} ───`)),
|
|
32
|
-
...envs.map((env) => ({
|
|
33
|
-
name: `${env.name.padEnd(20)} ${env.url}${env.isCurrent ? ' [current]' : ''}`,
|
|
34
|
-
value: `${project.id}::${env.name}`,
|
|
35
|
-
})),
|
|
36
|
-
]);
|
|
37
|
-
const current = entries.find(({ env }) => env.isCurrent);
|
|
38
|
-
const chosen = await select({
|
|
39
|
-
choices,
|
|
40
|
-
default: current && `${current.project.id}::${current.env.name}`,
|
|
41
|
-
message: 'Select project / environment',
|
|
42
|
-
});
|
|
43
|
-
const [projectId, envName] = chosen.split('::', 2);
|
|
44
|
-
const { project } = groups.find((group) => group.project.id === projectId);
|
|
45
|
-
return { envName, projectId, projectName: project.name };
|
|
13
|
+
const projectId = projects.length === 1
|
|
14
|
+
? projects[0].id
|
|
15
|
+
: await select({
|
|
16
|
+
choices: projects.map((project) => ({
|
|
17
|
+
name: `${project.name}${project.isCurrent ? ' [current]' : ''}`,
|
|
18
|
+
value: project.id,
|
|
19
|
+
})),
|
|
20
|
+
default: projects.find((project) => project.isCurrent)?.id,
|
|
21
|
+
message: 'Select project',
|
|
22
|
+
});
|
|
23
|
+
configManager.setCurrent(projectId);
|
|
24
|
+
const project = projects.find((candidate) => candidate.id === projectId);
|
|
25
|
+
this.log(`✓ Switched to "${project.name}"`);
|
|
46
26
|
}
|
|
47
27
|
}
|
package/dist/commands/pull.js
CHANGED
|
@@ -7,17 +7,21 @@ import { pluralize } from '../utils/pluralize.js';
|
|
|
7
7
|
const PULL_TARGETS = [
|
|
8
8
|
{ commandId: 'composer:pull', label: 'composer' },
|
|
9
9
|
{ commandId: 'plugin:pull', label: 'plugins' },
|
|
10
|
+
{ commandId: 'cpt:pull', label: 'post types', supportsYes: true },
|
|
11
|
+
{ commandId: 'taxonomy:pull', label: 'taxonomies', supportsYes: true },
|
|
12
|
+
{ commandId: 'meta:pull', label: 'post meta', supportsYes: true },
|
|
13
|
+
{ commandId: 'role:pull', label: 'roles', supportsYes: true },
|
|
10
14
|
{ commandId: 'acf:pull', label: 'ACF', supportsYes: true },
|
|
11
15
|
{ commandId: 'api:pull', label: 'API routes', supportsYes: true },
|
|
12
16
|
{ commandId: 'hook:pull', label: 'hooks', supportsYes: true },
|
|
13
17
|
{ commandId: 'form:pull', label: 'forms', supportsYes: true },
|
|
14
|
-
{ commandId: '
|
|
18
|
+
{ commandId: 'pattern:pull', label: 'patterns', supportsYes: true },
|
|
15
19
|
{ commandId: 'menu:pull', label: 'menus', supportsYes: true },
|
|
16
20
|
{ commandId: 'option:pull', label: 'options', supportsYes: true },
|
|
17
21
|
{ commandId: 'snippet:pull', label: 'snippets', supportsYes: true },
|
|
18
22
|
];
|
|
19
23
|
export default class Pull extends LoopressCommand {
|
|
20
|
-
static description = 'Pull all content, plugins, composer dependencies, ACF, API routes, hooks, forms,
|
|
24
|
+
static description = 'Pull all content, plugins, composer dependencies, post types, taxonomies, post meta, roles, ACF, API routes, hooks, forms, patterns, menus, options, and snippets, from WordPress';
|
|
21
25
|
static enableJsonFlag = true;
|
|
22
26
|
static examples = ['$ lps pull', '$ lps pull --env staging', '$ lps pull --dry-run'];
|
|
23
27
|
static flags = {
|
|
@@ -27,6 +31,7 @@ export default class Pull extends LoopressCommand {
|
|
|
27
31
|
failedCount = 0;
|
|
28
32
|
async run() {
|
|
29
33
|
const results = [];
|
|
34
|
+
this.warnRemovedSeoModule();
|
|
30
35
|
for (const target of PULL_TARGETS) {
|
|
31
36
|
this.log(`\n→ Pulling ${target.label}...`);
|
|
32
37
|
try {
|
package/dist/commands/push.js
CHANGED
|
@@ -12,20 +12,26 @@ import { pluralize } from '../utils/pluralize.js';
|
|
|
12
12
|
const PUSH_TARGETS = [
|
|
13
13
|
{ commandId: 'plugin:push', label: 'plugins' },
|
|
14
14
|
{ commandId: 'composer:push', label: 'composer' },
|
|
15
|
+
// Before ACF: a field group's location rule can target a Loopress post type or taxonomy.
|
|
16
|
+
{ commandId: 'cpt:push', label: 'post types' },
|
|
17
|
+
{ commandId: 'taxonomy:push', label: 'taxonomies' },
|
|
18
|
+
{ commandId: 'meta:push', label: 'post meta' },
|
|
19
|
+
{ commandId: 'role:push', label: 'roles' },
|
|
15
20
|
{ commandId: 'acf:push', label: 'ACF' },
|
|
16
21
|
{ commandId: 'api:push', label: 'API routes' },
|
|
17
22
|
{ commandId: 'hook:push', label: 'hooks' },
|
|
18
23
|
{ commandId: 'form:push', label: 'forms' },
|
|
24
|
+
// Before templates and pages, which can reference them (`wp:pattern`).
|
|
25
|
+
{ commandId: 'pattern:push', label: 'patterns' },
|
|
19
26
|
// Before pages: WordPress refuses a page whose `template` it doesn't know yet.
|
|
20
27
|
{ commandId: 'theme:template:push', label: 'templates and parts' },
|
|
21
28
|
{ commandId: 'page:push', label: 'pages' },
|
|
22
|
-
{ commandId: 'seo:push', label: 'SEO' },
|
|
23
29
|
{ commandId: 'menu:push', label: 'menus' },
|
|
24
30
|
{ commandId: 'option:push', label: 'options' },
|
|
25
31
|
{ commandId: 'snippet:push', label: 'snippets' },
|
|
26
32
|
];
|
|
27
33
|
export default class Push extends LoopressCommand {
|
|
28
|
-
static description = 'Push all local content, plugins, composer dependencies, ACF, API routes, hooks, forms, templates, pages,
|
|
34
|
+
static description = 'Push all local content, plugins, composer dependencies, post types, taxonomies, post meta, roles, ACF, API routes, hooks, forms, patterns, templates, pages, menus, options, and snippets, to WordPress';
|
|
29
35
|
static enableJsonFlag = true;
|
|
30
36
|
static examples = ['$ lps push', '$ lps push --env staging', '$ lps push --dry-run'];
|
|
31
37
|
static flags = {
|
|
@@ -35,6 +41,7 @@ export default class Push extends LoopressCommand {
|
|
|
35
41
|
failedCount = 0;
|
|
36
42
|
async run() {
|
|
37
43
|
await this.guardProductionPush();
|
|
44
|
+
this.warnRemovedSeoModule();
|
|
38
45
|
const argv = this.buildArgv();
|
|
39
46
|
const results = [];
|
|
40
47
|
const composerPushedByPlugins = await this.composerDeclaresPlugins();
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { resourceDiffCommand } from '../../lib/diff-command.js';
|
|
2
|
+
export default resourceDiffCommand('role', {
|
|
3
|
+
description: 'Show what differs in roles between your local roles/ files and a WordPress environment, or between two environments',
|
|
4
|
+
pathNoun: 'roles directory',
|
|
5
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { declaredListCommand } from '../../lib/declared-type-command.js';
|
|
2
|
+
import { ROLE_SPEC } from '../../utils/declared-type-format.js';
|
|
3
|
+
import { pluralize } from '../../utils/pluralize.js';
|
|
4
|
+
export default declaredListCommand(ROLE_SPEC, 'List the user roles on WordPress, where each comes from (Loopress, WordPress, a plugin) and how many users have it. Only Loopress ones are pulled and pushed.', (count) => pluralize(count, 'user'));
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { declaredPullCommand } from '../../lib/declared-type-command.js';
|
|
2
|
+
import { ROLE_SPEC } from '../../utils/declared-type-format.js';
|
|
3
|
+
export default declaredPullCommand(ROLE_SPEC, 'Pull the roles Loopress manages on WordPress into roles/<slug>.json files. ' +
|
|
4
|
+
'Built-in roles and roles created by a plugin are never pulled, see `lps role list`.');
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { declaredPushCommand } from '../../lib/declared-type-command.js';
|
|
2
|
+
import { ROLE_SPEC } from '../../utils/declared-type-format.js';
|
|
3
|
+
export default declaredPushCommand(ROLE_SPEC, 'Push local roles (roles/<slug>.json, a "name" and its "capabilities") to WordPress. ' +
|
|
4
|
+
'Create or update only, never deletes a role (use `lps role rm`), never changes which users have it.');
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { declaredRmCommand } from '../../lib/declared-type-command.js';
|
|
2
|
+
import { ROLE_SPEC } from '../../utils/declared-type-format.js';
|
|
3
|
+
export default declaredRmCommand(ROLE_SPEC, 'Stop registering a Loopress role on WordPress. Its users keep it in their profile, with no capability until the role is pushed again.', 'Its users keep it in their profile, with no capability until it is pushed again.');
|
|
@@ -18,8 +18,7 @@ export default class Status extends Command {
|
|
|
18
18
|
env: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
19
|
};
|
|
20
20
|
run(): Promise<StatusResult>;
|
|
21
|
-
private
|
|
22
|
-
private
|
|
23
|
-
private reportPinnedProject;
|
|
21
|
+
private logTarget;
|
|
22
|
+
private report;
|
|
24
23
|
}
|
|
25
24
|
export {};
|
package/dist/commands/status.js
CHANGED
|
@@ -11,52 +11,26 @@ export default class Status extends Command {
|
|
|
11
11
|
};
|
|
12
12
|
async run() {
|
|
13
13
|
const { flags } = await this.parse(Status);
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
if (flags.env) {
|
|
17
|
-
info = this.reportEnvOverride(localConfig.projectId, flags.env);
|
|
18
|
-
}
|
|
19
|
-
else if (localConfig.projectId) {
|
|
20
|
-
info = this.reportPinnedProject(localConfig.projectId);
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
info = this.reportActiveProject();
|
|
24
|
-
}
|
|
14
|
+
const { projectId } = await readLocalConfig();
|
|
15
|
+
const info = this.report(projectId, flags.env);
|
|
25
16
|
this.log('');
|
|
26
17
|
this.log(`Config dir: ${this.config.configDir}`);
|
|
27
18
|
this.log(`Data dir: ${this.config.dataDir}`);
|
|
28
19
|
return { ...info, configDir: this.config.configDir, dataDir: this.config.dataDir };
|
|
29
20
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const project = env ? configManager.getCurrentProject() : undefined;
|
|
33
|
-
if (!env || !project) {
|
|
34
|
-
const note = 'No project configured. Run `lps project config` first.';
|
|
35
|
-
this.log(note);
|
|
36
|
-
return { note };
|
|
37
|
-
}
|
|
38
|
-
this.log(`Project: ${project.name} (${env.name})`);
|
|
21
|
+
logTarget(label, env) {
|
|
22
|
+
this.log(`Project: ${label}`);
|
|
39
23
|
this.log(`URL: ${env.url}`);
|
|
40
|
-
return { environment: env.name, project:
|
|
24
|
+
return { environment: env.name, project: label, url: env.url };
|
|
41
25
|
}
|
|
42
|
-
// Mirrors base.ts:resolveEnvironment
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (!project) {
|
|
26
|
+
// Mirrors base.ts:resolveEnvironment, reporting instead of failing where it can.
|
|
27
|
+
report(pinnedProjectId, envName) {
|
|
28
|
+
const projectId = pinnedProjectId ?? configManager.getCurrentProject()?.id;
|
|
29
|
+
if (!projectId) {
|
|
47
30
|
const note = 'No project configured. Run `lps project config` first.';
|
|
48
31
|
this.log(note);
|
|
49
32
|
return { note };
|
|
50
33
|
}
|
|
51
|
-
const env = project.environments[envName];
|
|
52
|
-
if (!env) {
|
|
53
|
-
this.error(`Environment "${envName}" not found in project "${project.name}". Available: ${Object.keys(project.environments).join(', ')}`);
|
|
54
|
-
}
|
|
55
|
-
this.log(`Project: ${project.name} (${env.name}, via --env)`);
|
|
56
|
-
this.log(`URL: ${env.url}`);
|
|
57
|
-
return { environment: env.name, project: `${project.name} (${env.name}, via --env)`, url: env.url };
|
|
58
|
-
}
|
|
59
|
-
reportPinnedProject(projectId) {
|
|
60
34
|
const project = configManager.getProject(projectId);
|
|
61
35
|
if (!project) {
|
|
62
36
|
const note = `loopress.json pins project "${projectId}", but it no longer exists. Run \`lps project config\` to configure it.`;
|
|
@@ -71,28 +45,22 @@ export default class Status extends Command {
|
|
|
71
45
|
this.log(note);
|
|
72
46
|
return { note, project: project.name };
|
|
73
47
|
}
|
|
74
|
-
if (
|
|
75
|
-
const env = project.environments[
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
48
|
+
if (envName) {
|
|
49
|
+
const env = project.environments[envName];
|
|
50
|
+
if (!env) {
|
|
51
|
+
this.error(`Environment "${envName}" not found in project "${project.name}". Available: ${envNames.join(', ')}`);
|
|
52
|
+
}
|
|
53
|
+
return this.logTarget(`${project.name} (${env.name}, via --env)`, env);
|
|
79
54
|
}
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
this.log(`Project: ${project.name} ${c('yellow', '(ambiguous)')}`);
|
|
55
|
+
const env = configManager.getDefaultEnvironment(projectId);
|
|
56
|
+
if (!env) {
|
|
57
|
+
const note = `"${project.name}" has no "local" environment, pass --env to pick one.`;
|
|
58
|
+
this.log(`Project: ${project.name} ${c('yellow', '(no default environment)')}`);
|
|
85
59
|
this.log(`Environments: ${envNames.join(', ')}`);
|
|
86
60
|
this.log('');
|
|
87
|
-
this.warn(
|
|
88
|
-
this.log('Run `lps project switch` to pick one before running commands here.');
|
|
89
|
-
if (current) {
|
|
90
|
-
this.log(`(Globally active project right now: "${current.name}")`);
|
|
91
|
-
}
|
|
61
|
+
this.warn(note);
|
|
92
62
|
return { environments: envNames, note, project: project.name };
|
|
93
63
|
}
|
|
94
|
-
this.
|
|
95
|
-
this.log(`URL: ${currentEnv.url}`);
|
|
96
|
-
return { environment: currentEnv.name, project: `${project.name} (${currentEnv.name})`, url: currentEnv.url };
|
|
64
|
+
return this.logTarget(`${project.name} (${env.name})`, env);
|
|
97
65
|
}
|
|
98
66
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { resourceDiffCommand } from '../../lib/diff-command.js';
|
|
2
|
+
export default resourceDiffCommand('taxonomy', {
|
|
3
|
+
description: 'Show what differs in taxonomies between your local taxonomies/ files and a WordPress environment, or between two environments',
|
|
4
|
+
pathNoun: 'taxonomies directory',
|
|
5
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { declaredListCommand } from '../../lib/declared-type-command.js';
|
|
2
|
+
import { TAXONOMY_SPEC } from '../../utils/declared-type-format.js';
|
|
3
|
+
import { pluralize } from '../../utils/pluralize.js';
|
|
4
|
+
export default declaredListCommand(TAXONOMY_SPEC, 'List the taxonomies registered on WordPress, where each comes from (Loopress, WordPress, ACF, CPT UI, a theme or plugin) ' +
|
|
5
|
+
'and the post types it attaches to. Only Loopress ones are pulled and pushed.', (count) => pluralize(count, 'term'));
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { declaredPullCommand } from '../../lib/declared-type-command.js';
|
|
2
|
+
import { TAXONOMY_SPEC } from '../../utils/declared-type-format.js';
|
|
3
|
+
export default declaredPullCommand(TAXONOMY_SPEC, 'Pull the taxonomies Loopress manages on WordPress into taxonomies/<slug>.json files, never their terms. ' +
|
|
4
|
+
'Taxonomies registered by a theme or another plugin are never pulled, see `lps taxonomy list`.');
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { declaredPushCommand } from '../../lib/declared-type-command.js';
|
|
2
|
+
import { TAXONOMY_SPEC } from '../../utils/declared-type-format.js';
|
|
3
|
+
export default declaredPushCommand(TAXONOMY_SPEC, 'Push local taxonomies (taxonomies/<slug>.json, register_taxonomy() arguments plus "object_type") to WordPress. ' +
|
|
4
|
+
'Create or update only, never deletes a taxonomy (use `lps taxonomy rm`), never touches its terms.');
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { declaredRmCommand } from '../../lib/declared-type-command.js';
|
|
2
|
+
import { TAXONOMY_SPEC } from '../../utils/declared-type-format.js';
|
|
3
|
+
export default declaredRmCommand(TAXONOMY_SPEC, 'Stop registering a Loopress taxonomy on WordPress. Its terms are kept in the database, hidden until the taxonomy is pushed again.', "Its terms are kept in the database, hidden until it's pushed again.");
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { resourceRollbackCommand } from '../../lib/rollback-command.js';
|
|
2
|
+
export default resourceRollbackCommand('taxonomy', {
|
|
3
|
+
description: 'Restore taxonomies to a snapshot taken automatically before an earlier `lps taxonomy push`',
|
|
4
|
+
pathNoun: 'taxonomies directory',
|
|
5
|
+
});
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { PushCommand } from '../../../lib/push-command.js';
|
|
2
2
|
type PushResult = {
|
|
3
3
|
active?: boolean;
|
|
4
|
+
blockStyles: string[];
|
|
4
5
|
customized?: string[];
|
|
5
6
|
parts: string[];
|
|
6
7
|
status: 'dry-run' | 'success';
|
|
7
8
|
stylesheet?: string;
|
|
8
9
|
templates: string[];
|
|
10
|
+
unknownBlocks?: string[];
|
|
9
11
|
};
|
|
10
12
|
export default class Push extends PushCommand {
|
|
11
13
|
static description: string;
|
|
@@ -4,8 +4,9 @@ import { formatPageProblems } from '../../../utils/page-format.js';
|
|
|
4
4
|
import { pluralize } from '../../../utils/pluralize.js';
|
|
5
5
|
import { resolveResourceDir } from '../../../utils/resource-dirs.js';
|
|
6
6
|
import { CHILD_THEME_ENDPOINT, readLocalTemplates } from '../../../utils/template-format.js';
|
|
7
|
+
import { bundleThemeCss } from '../../../utils/theme-css.js';
|
|
7
8
|
export default class Push extends PushCommand {
|
|
8
|
-
static description = 'Push block templates (templates/<slug>.html)
|
|
9
|
+
static description = 'Push block templates (theme/templates/<slug>.html), template parts (theme/parts/<slug>.html) and the theme CSS (theme/css/style.css, theme/blocks/<namespace>/<block>.css, bundled with esbuild) as the files of a child theme of the active block theme, <parent>-loopress. The child mirrors the project: files removed locally are removed from it. It is never activated for you.';
|
|
9
10
|
static enableJsonFlag = true;
|
|
10
11
|
static examples = ['$ lps theme template push', '$ lps theme template push --dry-run'];
|
|
11
12
|
static flags = {
|
|
@@ -19,17 +20,24 @@ export default class Push extends PushCommand {
|
|
|
19
20
|
this.log(`Pushing templates and parts to ${this.siteConfig.url}`);
|
|
20
21
|
this.log(`Templates path: ${templatesPath}, parts path: ${partsPath}`);
|
|
21
22
|
const { parts, problems, templates } = await readLocalTemplates(templatesPath, partsPath);
|
|
23
|
+
const { css, problems: cssProblems } = await bundleThemeCss(resolveResourceDir('css', this.localConfig), resolveResourceDir('blockCss', this.localConfig));
|
|
24
|
+
problems.push(...cssProblems);
|
|
22
25
|
if (problems.length > 0) {
|
|
23
26
|
this.error(`Nothing was pushed, fix these files first:\n ${formatPageProblems(problems)}`);
|
|
24
27
|
}
|
|
25
|
-
const names = {
|
|
26
|
-
|
|
28
|
+
const names = {
|
|
29
|
+
blockStyles: css.blocks.map((block) => block.name),
|
|
30
|
+
parts: parts.map((part) => part.slug),
|
|
31
|
+
templates: templates.map((template) => template.slug),
|
|
32
|
+
};
|
|
33
|
+
this.log(`Found ${pluralize(templates.length, 'template')}, ${pluralize(parts.length, 'part')}, ` +
|
|
34
|
+
`${css.style ? 'a global stylesheet' : 'no global stylesheet'} and ${pluralize(css.blocks.length, 'block stylesheet')}`);
|
|
27
35
|
if (this.dryRun) {
|
|
28
36
|
await this.previewRemote(getResourceStateProvider('template'), templatesPath);
|
|
29
37
|
this.log('[dry-run] Would rewrite the child theme with them.');
|
|
30
38
|
return { ...names, status: 'dry-run' };
|
|
31
39
|
}
|
|
32
|
-
const child = await this.wp.put(CHILD_THEME_ENDPOINT, { parts, templates });
|
|
40
|
+
const child = await this.wp.put(CHILD_THEME_ENDPOINT, { ...css, parts, templates });
|
|
33
41
|
await this.recordSuccess();
|
|
34
42
|
this.log(`Child theme ${child.stylesheet} written.`);
|
|
35
43
|
if (!child.active) {
|
|
@@ -39,6 +47,19 @@ export default class Push extends PushCommand {
|
|
|
39
47
|
this.warn(`Edited in the Site Editor, their database copy still wins over the pushed file: ${child.customized.join(', ')}. ` +
|
|
40
48
|
'Clear their customizations in Appearance > Editor to use the pushed version.');
|
|
41
49
|
}
|
|
42
|
-
|
|
50
|
+
// A warning, never a refusal: the block may come from a plugin active on another environment.
|
|
51
|
+
const unknownBlocks = child.unknownBlocks ?? [];
|
|
52
|
+
if (unknownBlocks.length > 0) {
|
|
53
|
+
this.warn(`No block registered on this site under these names, their CSS will never load: ${unknownBlocks.join(', ')}. ` +
|
|
54
|
+
'Check the file name in theme/blocks/, or activate the plugin that provides the block.');
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
...names,
|
|
58
|
+
active: child.active,
|
|
59
|
+
customized: child.customized,
|
|
60
|
+
status: 'success',
|
|
61
|
+
stylesheet: child.stylesheet,
|
|
62
|
+
unknownBlocks,
|
|
63
|
+
};
|
|
43
64
|
}
|
|
44
65
|
}
|
|
@@ -8,15 +8,15 @@ export declare class ProjectConfigManager {
|
|
|
8
8
|
id: string;
|
|
9
9
|
});
|
|
10
10
|
getConfigFilePath(): string;
|
|
11
|
-
getCurrentEnv(): EnvironmentConfig | null;
|
|
12
11
|
getCurrentProject(): null | (ProjectConfig & {
|
|
13
12
|
id: string;
|
|
14
13
|
});
|
|
14
|
+
getDefaultEnvironment(projectId: string): EnvironmentConfig | null;
|
|
15
15
|
getEnvironment(projectId: string, envName: string): EnvironmentConfig | null;
|
|
16
16
|
getProject(id: string): null | ProjectConfig;
|
|
17
17
|
isTelemetryDisabled(): boolean;
|
|
18
18
|
listEnvironments(projectId: string): Array<EnvironmentConfig & {
|
|
19
|
-
|
|
19
|
+
isDefault: boolean;
|
|
20
20
|
}>;
|
|
21
21
|
listProjects(): Array<ProjectConfig & {
|
|
22
22
|
id: string;
|
|
@@ -26,7 +26,7 @@ export declare class ProjectConfigManager {
|
|
|
26
26
|
removeEnvironment(projectId: string, envName: string): void;
|
|
27
27
|
removeProject(id: string): void;
|
|
28
28
|
setConfigDir(configDir: string): void;
|
|
29
|
-
setCurrent(projectId: string
|
|
29
|
+
setCurrent(projectId: string): void;
|
|
30
30
|
setEnvironment(projectId: string, envName: string, env: EnvironmentConfig): void;
|
|
31
31
|
setEnvironmentApiId(projectId: string, envName: string, apiEnvironmentId: string): void;
|
|
32
32
|
setProject(id: string, project: ProjectConfig): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { LOCAL_ENVIRONMENT_NAME } from '../lib/wp-client.js';
|
|
3
4
|
import { toSlug } from '../utils/to-slug.js';
|
|
4
5
|
import { readJsonFile, writeJsonFileAtomic } from './json-file.js';
|
|
5
6
|
export class ProjectConfigManager {
|
|
@@ -39,15 +40,6 @@ export class ProjectConfigManager {
|
|
|
39
40
|
getConfigFilePath() {
|
|
40
41
|
return join(this.requireConfigDir(), 'config.json');
|
|
41
42
|
}
|
|
42
|
-
getCurrentEnv() {
|
|
43
|
-
const config = this.readConfig();
|
|
44
|
-
if (!config.currentProject)
|
|
45
|
-
return null;
|
|
46
|
-
const project = config.projects[config.currentProject.id];
|
|
47
|
-
if (!project)
|
|
48
|
-
return null;
|
|
49
|
-
return project.environments[config.currentProject.env] ?? null;
|
|
50
|
-
}
|
|
51
43
|
getCurrentProject() {
|
|
52
44
|
const config = this.readConfig();
|
|
53
45
|
if (!config.currentProject)
|
|
@@ -57,6 +49,17 @@ export class ProjectConfigManager {
|
|
|
57
49
|
return null;
|
|
58
50
|
return { ...project, id: config.currentProject.id };
|
|
59
51
|
}
|
|
52
|
+
// The environment a command targets without --env: "local" when the project has one (where
|
|
53
|
+
// you work, so it's the safe default), otherwise its only environment. Null when the project
|
|
54
|
+
// has several remote ones and no local: the user must say which one with --env, since a
|
|
55
|
+
// remembered choice is exactly the hidden state that sends a push to the wrong site.
|
|
56
|
+
getDefaultEnvironment(projectId) {
|
|
57
|
+
const environments = this.getProject(projectId)?.environments ?? {};
|
|
58
|
+
const names = Object.keys(environments);
|
|
59
|
+
if (Object.hasOwn(environments, LOCAL_ENVIRONMENT_NAME))
|
|
60
|
+
return environments[LOCAL_ENVIRONMENT_NAME];
|
|
61
|
+
return names.length === 1 ? environments[names[0]] : null;
|
|
62
|
+
}
|
|
60
63
|
getEnvironment(projectId, envName) {
|
|
61
64
|
const project = this.getProject(projectId);
|
|
62
65
|
if (!project)
|
|
@@ -71,14 +74,11 @@ export class ProjectConfigManager {
|
|
|
71
74
|
return this.readConfig().telemetry?.disabled ?? false;
|
|
72
75
|
}
|
|
73
76
|
listEnvironments(projectId) {
|
|
74
|
-
const
|
|
75
|
-
const project = config.projects[projectId];
|
|
77
|
+
const project = this.getProject(projectId);
|
|
76
78
|
if (!project)
|
|
77
79
|
return [];
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
isCurrent: config.currentProject?.id === projectId && config.currentProject.env === env.name,
|
|
81
|
-
}));
|
|
80
|
+
const defaultEnv = this.getDefaultEnvironment(projectId);
|
|
81
|
+
return Object.values(project.environments).map((env) => ({ ...env, isDefault: env.name === defaultEnv?.name }));
|
|
82
82
|
}
|
|
83
83
|
listProjects() {
|
|
84
84
|
const config = this.readConfig();
|
|
@@ -101,10 +101,6 @@ export class ProjectConfigManager {
|
|
|
101
101
|
if (!project)
|
|
102
102
|
return;
|
|
103
103
|
Reflect.deleteProperty(project.environments, envName);
|
|
104
|
-
if (config.currentProject?.id === projectId && config.currentProject.env === envName) {
|
|
105
|
-
const remaining = Object.keys(project.environments);
|
|
106
|
-
config.currentProject = remaining.length > 0 ? { env: remaining[0], id: projectId } : null;
|
|
107
|
-
}
|
|
108
104
|
this.writeConfig(config);
|
|
109
105
|
}
|
|
110
106
|
removeProject(id) {
|
|
@@ -112,9 +108,7 @@ export class ProjectConfigManager {
|
|
|
112
108
|
Reflect.deleteProperty(config.projects, id);
|
|
113
109
|
if (config.currentProject?.id === id) {
|
|
114
110
|
const [nextId] = Object.keys(config.projects);
|
|
115
|
-
|
|
116
|
-
const [nextEnv] = nextProject ? Object.keys(nextProject.environments) : [];
|
|
117
|
-
config.currentProject = nextId && nextEnv ? { env: nextEnv, id: nextId } : null;
|
|
111
|
+
config.currentProject = nextId ? { id: nextId } : null;
|
|
118
112
|
}
|
|
119
113
|
this.writeConfig(config);
|
|
120
114
|
}
|
|
@@ -124,11 +118,11 @@ export class ProjectConfigManager {
|
|
|
124
118
|
setConfigDir(configDir) {
|
|
125
119
|
this.configDir = configDir;
|
|
126
120
|
}
|
|
127
|
-
setCurrent(projectId
|
|
121
|
+
setCurrent(projectId) {
|
|
128
122
|
const config = this.readConfig();
|
|
129
123
|
if (!Object.hasOwn(config.projects, projectId))
|
|
130
124
|
return;
|
|
131
|
-
config.currentProject = {
|
|
125
|
+
config.currentProject = { id: projectId };
|
|
132
126
|
this.writeConfig(config);
|
|
133
127
|
}
|
|
134
128
|
setEnvironment(projectId, envName, env) {
|
|
@@ -137,7 +131,7 @@ export class ProjectConfigManager {
|
|
|
137
131
|
if (!project)
|
|
138
132
|
return;
|
|
139
133
|
project.environments[envName] = env;
|
|
140
|
-
config.currentProject ??= {
|
|
134
|
+
config.currentProject ??= { id: projectId };
|
|
141
135
|
this.writeConfig(config);
|
|
142
136
|
}
|
|
143
137
|
setEnvironmentApiId(projectId, envName, apiEnvironmentId) {
|
|
@@ -151,11 +145,7 @@ export class ProjectConfigManager {
|
|
|
151
145
|
setProject(id, project) {
|
|
152
146
|
const config = this.readConfig();
|
|
153
147
|
config.projects[id] = project;
|
|
154
|
-
|
|
155
|
-
const [firstEnv] = Object.keys(project.environments);
|
|
156
|
-
if (firstEnv)
|
|
157
|
-
config.currentProject = { env: firstEnv, id };
|
|
158
|
-
}
|
|
148
|
+
config.currentProject ??= { id };
|
|
159
149
|
this.writeConfig(config);
|
|
160
150
|
}
|
|
161
151
|
setProjectApiId(id, apiProjectId) {
|
|
@@ -203,7 +193,8 @@ export class ProjectConfigManager {
|
|
|
203
193
|
if (value === null || typeof value !== 'object')
|
|
204
194
|
return null;
|
|
205
195
|
const pointer = value;
|
|
206
|
-
|
|
196
|
+
// A legacy `env` field, from when switch also picked the environment, is dropped here.
|
|
197
|
+
return typeof pointer.id === 'string' ? { id: pointer.id } : null;
|
|
207
198
|
}
|
|
208
199
|
sanitizeProjects(value) {
|
|
209
200
|
if (typeof value !== 'object' || value === null)
|