@loopress/cli 0.6.0 → 0.7.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 +71 -90
- package/dist/commands/composer/pull.js +1 -1
- package/dist/commands/composer/push.js +1 -1
- package/dist/commands/init.js +15 -6
- package/dist/commands/login.js +1 -1
- package/dist/commands/logout.js +1 -1
- package/dist/commands/plugin/add.d.ts +0 -2
- package/dist/commands/plugin/add.js +2 -23
- package/dist/commands/plugin/pull.js +1 -1
- package/dist/commands/plugin/push.js +3 -3
- package/dist/commands/project/config.d.ts +1 -0
- package/dist/commands/project/config.js +36 -17
- package/dist/commands/project/list.js +4 -5
- package/dist/commands/project/remove.js +33 -14
- package/dist/commands/project/switch.d.ts +2 -0
- package/dist/commands/project/switch.js +28 -9
- package/dist/commands/snippet/list.js +3 -3
- package/dist/commands/snippet/pull.d.ts +1 -1
- package/dist/commands/snippet/pull.js +7 -6
- package/dist/commands/snippet/push.d.ts +2 -2
- package/dist/commands/snippet/push.js +47 -15
- package/dist/commands/{project/remove-env.d.ts → status.d.ts} +3 -1
- package/dist/commands/status.js +66 -0
- package/dist/config/project-config.manager.d.ts +17 -10
- package/dist/config/project-config.manager.js +91 -44
- package/dist/config/types.d.ts +5 -2
- package/dist/lib/base.js +13 -3
- package/dist/types/snippet.d.ts +2 -0
- package/dist/utils/snippet-plugin.d.ts +2 -1
- package/dist/utils/snippet-plugin.js +5 -4
- package/oclif.manifest.json +149 -175
- package/package.json +17 -2
- package/dist/commands/project/remove-env.js +0 -33
- package/dist/commands/project/switch-env.d.ts +0 -6
- package/dist/commands/project/switch-env.js +0 -33
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
3
|
import { homedir } from 'node:os';
|
|
3
4
|
import { join } from 'node:path';
|
|
@@ -13,6 +14,9 @@ export class ProjectConfigManager {
|
|
|
13
14
|
}
|
|
14
15
|
return ProjectConfigManager.instance;
|
|
15
16
|
}
|
|
17
|
+
createProjectId() {
|
|
18
|
+
return randomUUID();
|
|
19
|
+
}
|
|
16
20
|
ensureConfigDir() {
|
|
17
21
|
const dir = join(this.homeDir, '.loopress');
|
|
18
22
|
if (!existsSync(dir)) {
|
|
@@ -23,41 +27,49 @@ export class ProjectConfigManager {
|
|
|
23
27
|
return join(this.homeDir, '.loopress', 'config.json');
|
|
24
28
|
}
|
|
25
29
|
getCurrentEnv() {
|
|
26
|
-
const
|
|
27
|
-
if (!
|
|
30
|
+
const config = this.readConfig();
|
|
31
|
+
if (!config.currentProject)
|
|
32
|
+
return null;
|
|
33
|
+
const project = config.projects[config.currentProject.id];
|
|
34
|
+
if (!project)
|
|
28
35
|
return null;
|
|
29
|
-
return project.environments[
|
|
36
|
+
return project.environments[config.currentProject.env] ?? null;
|
|
30
37
|
}
|
|
31
38
|
getCurrentProject() {
|
|
32
39
|
const config = this.readConfig();
|
|
33
|
-
if (!config.currentProject
|
|
40
|
+
if (!config.currentProject)
|
|
41
|
+
return null;
|
|
42
|
+
const project = config.projects[config.currentProject.id];
|
|
43
|
+
if (!project)
|
|
34
44
|
return null;
|
|
35
|
-
return config.
|
|
45
|
+
return { ...project, id: config.currentProject.id };
|
|
36
46
|
}
|
|
37
|
-
getEnvironment(
|
|
38
|
-
const project = this.getProject(
|
|
47
|
+
getEnvironment(projectId, envName) {
|
|
48
|
+
const project = this.getProject(projectId);
|
|
39
49
|
if (!project)
|
|
40
50
|
return null;
|
|
41
51
|
return project.environments[envName] ?? null;
|
|
42
52
|
}
|
|
43
|
-
getProject(
|
|
53
|
+
getProject(id) {
|
|
44
54
|
const config = this.readConfig();
|
|
45
|
-
return config.projects[
|
|
55
|
+
return config.projects[id] ?? null;
|
|
46
56
|
}
|
|
47
|
-
listEnvironments(
|
|
48
|
-
const
|
|
57
|
+
listEnvironments(projectId) {
|
|
58
|
+
const config = this.readConfig();
|
|
59
|
+
const project = config.projects[projectId];
|
|
49
60
|
if (!project)
|
|
50
61
|
return [];
|
|
51
62
|
return Object.values(project.environments).map((env) => ({
|
|
52
63
|
...env,
|
|
53
|
-
isCurrent:
|
|
64
|
+
isCurrent: config.currentProject?.id === projectId && config.currentProject.env === env.name,
|
|
54
65
|
}));
|
|
55
66
|
}
|
|
56
67
|
listProjects() {
|
|
57
68
|
const config = this.readConfig();
|
|
58
|
-
return Object.
|
|
69
|
+
return Object.entries(config.projects).map(([id, project]) => ({
|
|
59
70
|
...project,
|
|
60
|
-
|
|
71
|
+
id,
|
|
72
|
+
isCurrent: config.currentProject?.id === id,
|
|
61
73
|
}));
|
|
62
74
|
}
|
|
63
75
|
readConfig() {
|
|
@@ -65,58 +77,62 @@ export class ProjectConfigManager {
|
|
|
65
77
|
if (!existsSync(filePath)) {
|
|
66
78
|
return { currentProject: null, projects: {} };
|
|
67
79
|
}
|
|
68
|
-
|
|
80
|
+
try {
|
|
81
|
+
const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
82
|
+
return this.sanitizeConfig(parsed);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return { currentProject: null, projects: {} };
|
|
86
|
+
}
|
|
69
87
|
}
|
|
70
|
-
removeEnvironment(
|
|
88
|
+
removeEnvironment(projectId, envName) {
|
|
71
89
|
const config = this.readConfig();
|
|
72
|
-
|
|
90
|
+
const project = config.projects[projectId];
|
|
91
|
+
if (!project)
|
|
73
92
|
return;
|
|
74
|
-
const project = config.projects[projectName];
|
|
75
93
|
delete project.environments[envName];
|
|
76
|
-
if (
|
|
94
|
+
if (config.currentProject?.id === projectId && config.currentProject.env === envName) {
|
|
77
95
|
const remaining = Object.keys(project.environments);
|
|
78
|
-
|
|
96
|
+
config.currentProject = remaining.length > 0 ? { env: remaining[0], id: projectId } : null;
|
|
79
97
|
}
|
|
80
98
|
this.writeConfig(config);
|
|
81
99
|
}
|
|
82
|
-
removeProject(
|
|
100
|
+
removeProject(id) {
|
|
83
101
|
const config = this.readConfig();
|
|
84
|
-
delete config.projects[
|
|
85
|
-
if (config.currentProject ===
|
|
86
|
-
const
|
|
87
|
-
|
|
102
|
+
delete config.projects[id];
|
|
103
|
+
if (config.currentProject?.id === id) {
|
|
104
|
+
const [nextId] = Object.keys(config.projects);
|
|
105
|
+
const nextProject = nextId ? config.projects[nextId] : undefined;
|
|
106
|
+
const [nextEnv] = nextProject ? Object.keys(nextProject.environments) : [];
|
|
107
|
+
config.currentProject = nextId && nextEnv ? { env: nextEnv, id: nextId } : null;
|
|
88
108
|
}
|
|
89
109
|
this.writeConfig(config);
|
|
90
110
|
}
|
|
91
|
-
|
|
111
|
+
setCurrent(projectId, envName) {
|
|
92
112
|
const config = this.readConfig();
|
|
93
|
-
if (!config.projects[
|
|
113
|
+
if (!config.projects[projectId])
|
|
94
114
|
return;
|
|
95
|
-
config.
|
|
115
|
+
config.currentProject = { env: envName, id: projectId };
|
|
96
116
|
this.writeConfig(config);
|
|
97
117
|
}
|
|
98
|
-
|
|
118
|
+
setEnvironment(projectId, envName, env) {
|
|
99
119
|
const config = this.readConfig();
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
setEnvironment(projectName, envName, env) {
|
|
104
|
-
const config = this.readConfig();
|
|
105
|
-
if (!config.projects[projectName])
|
|
120
|
+
const project = config.projects[projectId];
|
|
121
|
+
if (!project)
|
|
106
122
|
return;
|
|
107
|
-
const project = config.projects[projectName];
|
|
108
|
-
const isFirst = Object.keys(project.environments).length === 0;
|
|
109
123
|
project.environments[envName] = env;
|
|
110
|
-
if (
|
|
111
|
-
|
|
124
|
+
if (!config.currentProject)
|
|
125
|
+
config.currentProject = { env: envName, id: projectId };
|
|
112
126
|
this.writeConfig(config);
|
|
113
127
|
}
|
|
114
|
-
setProject(
|
|
128
|
+
setProject(id, project) {
|
|
115
129
|
const config = this.readConfig();
|
|
116
|
-
|
|
117
|
-
config.
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
config.projects[id] = project;
|
|
131
|
+
if (!config.currentProject) {
|
|
132
|
+
const [firstEnv] = Object.keys(project.environments);
|
|
133
|
+
if (firstEnv)
|
|
134
|
+
config.currentProject = { env: firstEnv, id };
|
|
135
|
+
}
|
|
120
136
|
this.writeConfig(config);
|
|
121
137
|
}
|
|
122
138
|
writeConfig(config) {
|
|
@@ -126,5 +142,36 @@ export class ProjectConfigManager {
|
|
|
126
142
|
writeFileSync(tmpPath, JSON.stringify(config, null, 2));
|
|
127
143
|
renameSync(tmpPath, filePath);
|
|
128
144
|
}
|
|
145
|
+
isProjectConfig(value) {
|
|
146
|
+
if (typeof value !== 'object' || value === null)
|
|
147
|
+
return false;
|
|
148
|
+
const candidate = value;
|
|
149
|
+
return typeof candidate.name === 'string' && typeof candidate.environments === 'object' && candidate.environments !== null;
|
|
150
|
+
}
|
|
151
|
+
sanitizeConfig(value) {
|
|
152
|
+
if (typeof value !== 'object' || value === null)
|
|
153
|
+
return { currentProject: null, projects: {} };
|
|
154
|
+
const candidate = value;
|
|
155
|
+
return {
|
|
156
|
+
currentProject: this.sanitizeCurrentProject(candidate.currentProject),
|
|
157
|
+
projects: this.sanitizeProjects(candidate.projects),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
sanitizeCurrentProject(value) {
|
|
161
|
+
if (value === null || typeof value !== 'object')
|
|
162
|
+
return null;
|
|
163
|
+
const pointer = value;
|
|
164
|
+
return typeof pointer.id === 'string' && typeof pointer.env === 'string' ? { env: pointer.env, id: pointer.id } : null;
|
|
165
|
+
}
|
|
166
|
+
sanitizeProjects(value) {
|
|
167
|
+
if (typeof value !== 'object' || value === null)
|
|
168
|
+
return {};
|
|
169
|
+
const projects = {};
|
|
170
|
+
for (const [id, project] of Object.entries(value)) {
|
|
171
|
+
if (this.isProjectConfig(project))
|
|
172
|
+
projects[id] = project;
|
|
173
|
+
}
|
|
174
|
+
return projects;
|
|
175
|
+
}
|
|
129
176
|
}
|
|
130
177
|
export const configManager = ProjectConfigManager.getInstance();
|
package/dist/config/types.d.ts
CHANGED
|
@@ -6,11 +6,14 @@ export interface EnvironmentConfig {
|
|
|
6
6
|
}
|
|
7
7
|
export interface ProjectConfig {
|
|
8
8
|
addedAt: string;
|
|
9
|
-
currentEnv: null | string;
|
|
10
9
|
environments: Record<string, EnvironmentConfig>;
|
|
11
10
|
name: string;
|
|
12
11
|
}
|
|
12
|
+
export interface CurrentProjectPointer {
|
|
13
|
+
env: string;
|
|
14
|
+
id: string;
|
|
15
|
+
}
|
|
13
16
|
export interface LoopressConfig {
|
|
14
|
-
currentProject:
|
|
17
|
+
currentProject: CurrentProjectPointer | null;
|
|
15
18
|
projects: Record<string, ProjectConfig>;
|
|
16
19
|
}
|
package/dist/lib/base.js
CHANGED
|
@@ -33,10 +33,20 @@ export class LoopressCommand extends Command {
|
|
|
33
33
|
if (!project) {
|
|
34
34
|
this.error(`Project "${localConfig.projectId}" (from loopress.json) not found. Run \`lps project config\` to configure it.`);
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
const envNames = Object.keys(project.environments);
|
|
37
|
+
if (envNames.length === 0) {
|
|
38
|
+
this.error(`Project "${project.name}" has no environments configured. Run \`lps project config\` to add one.`);
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
+
if (envNames.length === 1) {
|
|
41
|
+
this.siteConfig = project.environments[envNames[0]];
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const current = configManager.getCurrentProject();
|
|
45
|
+
const currentEnv = current?.id === localConfig.projectId ? configManager.getCurrentEnv() : null;
|
|
46
|
+
if (!currentEnv) {
|
|
47
|
+
this.error(`Project "${project.name}" has multiple environments. Run \`lps project switch\` to pick one.`);
|
|
48
|
+
}
|
|
49
|
+
this.siteConfig = currentEnv;
|
|
40
50
|
return;
|
|
41
51
|
}
|
|
42
52
|
const env = configManager.getCurrentEnv();
|
package/dist/types/snippet.d.ts
CHANGED
|
@@ -9,9 +9,10 @@ export interface NormalizedSnippet {
|
|
|
9
9
|
tags: string[];
|
|
10
10
|
type: SnippetType;
|
|
11
11
|
}
|
|
12
|
+
export declare function parseType(raw: unknown): null | SnippetType;
|
|
12
13
|
export interface SnippetPlugin {
|
|
13
14
|
endpoint(siteUrl: string): string;
|
|
14
15
|
fromRemote(data: Record<string, unknown>): NormalizedSnippet;
|
|
15
|
-
toPayload(name: string, code: string, path: string): Record<string, unknown>;
|
|
16
|
+
toPayload(name: string, code: string, path: string, type: SnippetType): Record<string, unknown>;
|
|
16
17
|
}
|
|
17
18
|
export declare function getSnippetPlugin(name: PluginName): SnippetPlugin;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function parseType(raw) {
|
|
1
|
+
export function parseType(raw) {
|
|
2
2
|
const valid = ['css', 'html', 'js', 'php', 'text'];
|
|
3
3
|
const value = String(raw ?? '').toLowerCase();
|
|
4
4
|
return valid.includes(value) ? value : null;
|
|
@@ -29,12 +29,13 @@ class CodeSnippetsPlugin {
|
|
|
29
29
|
type: resolveType(data.type, String(data.code ?? '')),
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
-
toPayload(name, code, path) {
|
|
32
|
+
toPayload(name, code, path, type) {
|
|
33
33
|
return {
|
|
34
34
|
code: code.replace(/^<\?php\s*/i, ''),
|
|
35
35
|
desc: `Imported from ${path}`,
|
|
36
36
|
name,
|
|
37
37
|
tags: ['cli-import'],
|
|
38
|
+
type,
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
41
|
}
|
|
@@ -53,13 +54,13 @@ class WPCodePlugin {
|
|
|
53
54
|
type: resolveType(data.type, String(data.code ?? '')),
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
|
-
toPayload(name, code, path) {
|
|
57
|
+
toPayload(name, code, path, type) {
|
|
57
58
|
return {
|
|
58
59
|
code,
|
|
59
60
|
note: `Imported from ${path}`,
|
|
60
61
|
tags: ['cli-import'],
|
|
61
62
|
title: name,
|
|
62
|
-
type
|
|
63
|
+
type,
|
|
63
64
|
};
|
|
64
65
|
}
|
|
65
66
|
}
|