@objectstack/cli 2.0.6 → 2.0.7
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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +13 -0
- package/README.md +88 -3
- package/dist/bin.js +361 -19
- package/dist/index.d.ts +14 -1
- package/dist/index.js +422 -88
- package/package.json +9 -9
- package/src/bin.ts +18 -1
- package/src/commands/plugin.ts +372 -0
- package/src/index.ts +2 -0
- package/src/utils/plugin-commands.ts +163 -0
- package/test/commands.test.ts +6 -0
- package/test/plugin-commands.test.ts +162 -0
- package/test/plugin.test.ts +173 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/cli@2.0.
|
|
2
|
+
> @objectstack/cli@2.0.7 build /home/runner/work/spec/spec/packages/cli
|
|
3
3
|
> tsup
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/bin.ts
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
[34mESM[39m Build start
|
|
16
16
|
[34mCLI[39m Cleaning output folder
|
|
17
17
|
[34mESM[39m Build start
|
|
18
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
19
|
-
[32mESM[39m ⚡️ Build success in
|
|
20
|
-
[32mESM[39m [1mdist/bin.js [22m[
|
|
21
|
-
[32mESM[39m ⚡️ Build success in
|
|
18
|
+
[32mESM[39m [1mdist/index.js [22m[32m71.41 KB[39m
|
|
19
|
+
[32mESM[39m ⚡️ Build success in 166ms
|
|
20
|
+
[32mESM[39m [1mdist/bin.js [22m[32m74.36 KB[39m
|
|
21
|
+
[32mESM[39m ⚡️ Build success in 178ms
|
|
22
22
|
[34mDTS[39m Build start
|
|
23
|
-
[32mDTS[39m ⚡️ Build success in
|
|
24
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
23
|
+
[32mDTS[39m ⚡️ Build success in 9654ms
|
|
24
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m3.42 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @objectstack/cli
|
|
2
2
|
|
|
3
|
+
## 2.0.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @objectstack/spec@2.0.7
|
|
9
|
+
- @objectstack/core@2.0.7
|
|
10
|
+
- @objectstack/objectql@2.0.7
|
|
11
|
+
- @objectstack/driver-memory@2.0.7
|
|
12
|
+
- @objectstack/plugin-hono-server@2.0.7
|
|
13
|
+
- @objectstack/rest@2.0.7
|
|
14
|
+
- @objectstack/runtime@2.0.7
|
|
15
|
+
|
|
3
16
|
## 2.0.6
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -58,6 +58,15 @@ os compile
|
|
|
58
58
|
|
|
59
59
|
Available generate types: `object`, `view`, `action`, `flow`, `agent`, `dashboard`, `app`
|
|
60
60
|
|
|
61
|
+
### Plugin Management
|
|
62
|
+
|
|
63
|
+
| Command | Description |
|
|
64
|
+
|---------|-------------|
|
|
65
|
+
| `os plugin list [config]` | List plugins defined in configuration (alias: `os plugin ls`) |
|
|
66
|
+
| `os plugin info <name> [config]` | Show detailed plugin information |
|
|
67
|
+
| `os plugin add <package>` | Add a plugin import and entry to config |
|
|
68
|
+
| `os plugin remove <name>` | Remove a plugin from config (alias: `os plugin rm`) |
|
|
69
|
+
|
|
61
70
|
### Quality
|
|
62
71
|
|
|
63
72
|
| Command | Description |
|
|
@@ -118,6 +127,81 @@ export default defineStack({
|
|
|
118
127
|
- `-d, --dir <directory>` — Override target directory
|
|
119
128
|
- `--dry-run` — Preview without writing files
|
|
120
129
|
|
|
130
|
+
### `os plugin list`
|
|
131
|
+
|
|
132
|
+
- `--json` — Output as JSON
|
|
133
|
+
|
|
134
|
+
### `os plugin add`
|
|
135
|
+
|
|
136
|
+
- `-d, --dev` — Add as a dev-only plugin
|
|
137
|
+
- `-c, --config <path>` — Configuration file path
|
|
138
|
+
|
|
139
|
+
### `os plugin remove`
|
|
140
|
+
|
|
141
|
+
- `-c, --config <path>` — Configuration file path
|
|
142
|
+
|
|
143
|
+
## Plugin CLI Extensions
|
|
144
|
+
|
|
145
|
+
Plugins can extend the CLI with custom commands via the `contributes.commands` manifest field. The CLI automatically discovers and loads these commands at startup.
|
|
146
|
+
|
|
147
|
+
### How to Create a CLI Plugin
|
|
148
|
+
|
|
149
|
+
**1. Declare commands in the plugin manifest:**
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
export default defineStack({
|
|
153
|
+
manifest: {
|
|
154
|
+
id: 'com.acme.marketplace',
|
|
155
|
+
version: '1.0.0',
|
|
156
|
+
type: 'plugin',
|
|
157
|
+
name: 'Marketplace Plugin',
|
|
158
|
+
contributes: {
|
|
159
|
+
commands: [
|
|
160
|
+
{
|
|
161
|
+
name: 'marketplace',
|
|
162
|
+
description: 'Manage marketplace applications',
|
|
163
|
+
module: './dist/cli.js',
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**2. Export Commander.js commands from the module:**
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
// src/cli.ts
|
|
175
|
+
import { Command } from 'commander';
|
|
176
|
+
|
|
177
|
+
const marketplaceCommand = new Command('marketplace')
|
|
178
|
+
.description('Manage marketplace applications')
|
|
179
|
+
.addCommand(
|
|
180
|
+
new Command('search')
|
|
181
|
+
.argument('<query>')
|
|
182
|
+
.action(async (query) => { /* ... */ })
|
|
183
|
+
)
|
|
184
|
+
.addCommand(
|
|
185
|
+
new Command('install')
|
|
186
|
+
.argument('<app>')
|
|
187
|
+
.action(async (app) => { /* ... */ })
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// Named export (recommended)
|
|
191
|
+
export const commands = [marketplaceCommand];
|
|
192
|
+
// Also supports: export default Command | Command[]
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**3. Register the plugin in the host project and use:**
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
os plugin add @acme/plugin-marketplace
|
|
199
|
+
os marketplace search "crm"
|
|
200
|
+
os marketplace install com.acme.crm
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
For a complete guide, see the [Plugin CLI Extensions](/docs/guides/plugins#cli-command-extensions) section in the Plugins guide.
|
|
204
|
+
|
|
121
205
|
### `os info`
|
|
122
206
|
|
|
123
207
|
- `--json` — Output as JSON
|
|
@@ -133,9 +217,10 @@ os init # 1. Create project
|
|
|
133
217
|
os generate object customer # 2. Add a Customer object
|
|
134
218
|
os generate object order # 3. Add an Order object
|
|
135
219
|
os generate view customer # 4. Add a list view
|
|
136
|
-
os
|
|
137
|
-
os
|
|
138
|
-
os
|
|
220
|
+
os plugin add @objectstack/plugin-auth # 5. Add auth plugin
|
|
221
|
+
os validate # 6. Validate everything
|
|
222
|
+
os dev # 7. Start dev server
|
|
223
|
+
os compile # 8. Build for production
|
|
139
224
|
```
|
|
140
225
|
|
|
141
226
|
## License
|
package/dist/bin.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/bin.ts
|
|
4
4
|
import { createRequire as createRequire2 } from "module";
|
|
5
|
-
import { Command as
|
|
6
|
-
import
|
|
5
|
+
import { Command as Command13 } from "commander";
|
|
6
|
+
import chalk15 from "chalk";
|
|
7
7
|
|
|
8
8
|
// src/commands/compile.ts
|
|
9
9
|
import { Command } from "commander";
|
|
@@ -69,10 +69,10 @@ function formatZodErrors(error) {
|
|
|
69
69
|
console.log(chalk.bold.red(`
|
|
70
70
|
${section}:`));
|
|
71
71
|
for (const issue of sectionIssues) {
|
|
72
|
-
const
|
|
72
|
+
const path12 = issue.path?.join(".") || "";
|
|
73
73
|
const code = issue.code || "";
|
|
74
74
|
const msg = issue.message || "";
|
|
75
|
-
console.log(chalk.red(` \u2717 ${
|
|
75
|
+
console.log(chalk.red(` \u2717 ${path12}`));
|
|
76
76
|
console.log(chalk.dim(` ${code}: ${msg}`));
|
|
77
77
|
if (issue.expected) {
|
|
78
78
|
console.log(chalk.dim(` expected: ${chalk.green(issue.expected)}`));
|
|
@@ -1874,33 +1874,366 @@ var generateCommand = new Command11("generate").alias("g").description("Generate
|
|
|
1874
1874
|
}
|
|
1875
1875
|
});
|
|
1876
1876
|
|
|
1877
|
+
// src/commands/plugin.ts
|
|
1878
|
+
import { Command as Command12 } from "commander";
|
|
1879
|
+
import chalk13 from "chalk";
|
|
1880
|
+
import fs11 from "fs";
|
|
1881
|
+
import path11 from "path";
|
|
1882
|
+
function resolvePluginName(plugin) {
|
|
1883
|
+
if (typeof plugin === "string") return plugin;
|
|
1884
|
+
if (plugin && typeof plugin === "object") {
|
|
1885
|
+
const p = plugin;
|
|
1886
|
+
if (typeof p.name === "string") return p.name;
|
|
1887
|
+
if (p.constructor && p.constructor.name !== "Object") return p.constructor.name;
|
|
1888
|
+
}
|
|
1889
|
+
return "unknown";
|
|
1890
|
+
}
|
|
1891
|
+
function resolvePluginVersion(plugin) {
|
|
1892
|
+
if (plugin && typeof plugin === "object") {
|
|
1893
|
+
const p = plugin;
|
|
1894
|
+
if (typeof p.version === "string") return p.version;
|
|
1895
|
+
}
|
|
1896
|
+
return "-";
|
|
1897
|
+
}
|
|
1898
|
+
function resolvePluginType(plugin) {
|
|
1899
|
+
if (plugin && typeof plugin === "object") {
|
|
1900
|
+
const p = plugin;
|
|
1901
|
+
if (typeof p.type === "string") return p.type;
|
|
1902
|
+
}
|
|
1903
|
+
return "standard";
|
|
1904
|
+
}
|
|
1905
|
+
function readConfigText(configPath) {
|
|
1906
|
+
return fs11.readFileSync(configPath, "utf-8");
|
|
1907
|
+
}
|
|
1908
|
+
function addPluginToConfig(configPath, packageName) {
|
|
1909
|
+
let content = readConfigText(configPath);
|
|
1910
|
+
const shortName = packageName.replace(/^@[^/]+\//, "").replace(/^plugin-/, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
1911
|
+
const varName = shortName.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) + "Plugin";
|
|
1912
|
+
const importLine = `import ${varName} from '${packageName}';
|
|
1913
|
+
`;
|
|
1914
|
+
if (content.includes(packageName)) {
|
|
1915
|
+
throw new Error(`Plugin '${packageName}' is already referenced in the config`);
|
|
1916
|
+
}
|
|
1917
|
+
const importRegex = /^import .+$/gm;
|
|
1918
|
+
let lastImportEnd = 0;
|
|
1919
|
+
let match;
|
|
1920
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
1921
|
+
lastImportEnd = match.index + match[0].length;
|
|
1922
|
+
}
|
|
1923
|
+
if (lastImportEnd > 0) {
|
|
1924
|
+
content = content.slice(0, lastImportEnd) + "\n" + importLine + content.slice(lastImportEnd);
|
|
1925
|
+
} else {
|
|
1926
|
+
content = importLine + "\n" + content;
|
|
1927
|
+
}
|
|
1928
|
+
if (/plugins\s*:\s*\[/.test(content)) {
|
|
1929
|
+
let replaced = false;
|
|
1930
|
+
content = content.replace(
|
|
1931
|
+
/(plugins\s*:\s*\[)/,
|
|
1932
|
+
(match2) => {
|
|
1933
|
+
if (replaced) return match2;
|
|
1934
|
+
replaced = true;
|
|
1935
|
+
return `${match2}
|
|
1936
|
+
${varName},`;
|
|
1937
|
+
}
|
|
1938
|
+
);
|
|
1939
|
+
} else {
|
|
1940
|
+
content = content.replace(
|
|
1941
|
+
/(defineStack\(\{[\s\S]*?)(}\s*\))/,
|
|
1942
|
+
`$1 plugins: [
|
|
1943
|
+
${varName},
|
|
1944
|
+
],
|
|
1945
|
+
$2`
|
|
1946
|
+
);
|
|
1947
|
+
}
|
|
1948
|
+
fs11.writeFileSync(configPath, content);
|
|
1949
|
+
}
|
|
1950
|
+
function removePluginFromConfig(configPath, pluginName) {
|
|
1951
|
+
let content = readConfigText(configPath);
|
|
1952
|
+
const importRegex = new RegExp(`^import .+['"]${escapeRegex(pluginName)}['"]\\s*;?\\s*$\\n?`, "gm");
|
|
1953
|
+
const hadImport = importRegex.test(content);
|
|
1954
|
+
importRegex.lastIndex = 0;
|
|
1955
|
+
content = content.replace(importRegex, "");
|
|
1956
|
+
const shortName = pluginName.replace(/^@[^/]+\//, "").replace(/^plugin-/, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
1957
|
+
const varName = shortName.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) + "Plugin";
|
|
1958
|
+
if (!hadImport) {
|
|
1959
|
+
const varImportRegex = new RegExp(`^import .* ${escapeRegex(varName)} .+$\\n?`, "gm");
|
|
1960
|
+
content = content.replace(varImportRegex, "");
|
|
1961
|
+
}
|
|
1962
|
+
const entryPatterns = [
|
|
1963
|
+
new RegExp(`\\s*${escapeRegex(varName)},?\\n?`, "g"),
|
|
1964
|
+
new RegExp(`\\s*['"]${escapeRegex(pluginName)}['"],?\\n?`, "g")
|
|
1965
|
+
];
|
|
1966
|
+
for (const pattern of entryPatterns) {
|
|
1967
|
+
content = content.replace(pattern, "\n");
|
|
1968
|
+
}
|
|
1969
|
+
content = content.replace(/plugins\s*:\s*\[\s*\],?\n?/g, "");
|
|
1970
|
+
fs11.writeFileSync(configPath, content);
|
|
1971
|
+
}
|
|
1972
|
+
function escapeRegex(str) {
|
|
1973
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1974
|
+
}
|
|
1975
|
+
var listCommand = new Command12("list").alias("ls").description("List plugins defined in the configuration").argument("[config]", "Configuration file path").option("--json", "Output as JSON").action(async (configSource, options) => {
|
|
1976
|
+
try {
|
|
1977
|
+
const { config } = await loadConfig(configSource);
|
|
1978
|
+
const plugins = config.plugins || [];
|
|
1979
|
+
const devPlugins = config.devPlugins || [];
|
|
1980
|
+
if (options?.json) {
|
|
1981
|
+
const data = {
|
|
1982
|
+
plugins: plugins.map((p) => ({
|
|
1983
|
+
name: resolvePluginName(p),
|
|
1984
|
+
version: resolvePluginVersion(p),
|
|
1985
|
+
type: resolvePluginType(p),
|
|
1986
|
+
dev: false
|
|
1987
|
+
})),
|
|
1988
|
+
devPlugins: devPlugins.map((p) => ({
|
|
1989
|
+
name: resolvePluginName(p),
|
|
1990
|
+
version: resolvePluginVersion(p),
|
|
1991
|
+
type: resolvePluginType(p),
|
|
1992
|
+
dev: true
|
|
1993
|
+
}))
|
|
1994
|
+
};
|
|
1995
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1998
|
+
printHeader("Plugins");
|
|
1999
|
+
if (plugins.length === 0 && devPlugins.length === 0) {
|
|
2000
|
+
printInfo("No plugins configured");
|
|
2001
|
+
console.log("");
|
|
2002
|
+
console.log(chalk13.dim(" Hint: Add plugins to your objectstack.config.ts"));
|
|
2003
|
+
console.log(chalk13.dim(" Or run: os plugin add <package-name>"));
|
|
2004
|
+
console.log("");
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
if (plugins.length > 0) {
|
|
2008
|
+
console.log(chalk13.bold(`
|
|
2009
|
+
Plugins (${plugins.length}):`));
|
|
2010
|
+
for (const plugin of plugins) {
|
|
2011
|
+
const name = resolvePluginName(plugin);
|
|
2012
|
+
const version = resolvePluginVersion(plugin);
|
|
2013
|
+
const type = resolvePluginType(plugin);
|
|
2014
|
+
console.log(
|
|
2015
|
+
` ${chalk13.cyan("\u25CF")} ${chalk13.white(name)}` + (version !== "-" ? chalk13.dim(` v${version}`) : "") + (type !== "standard" ? chalk13.dim(` [${type}]`) : "")
|
|
2016
|
+
);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
if (devPlugins.length > 0) {
|
|
2020
|
+
console.log(chalk13.bold(`
|
|
2021
|
+
Dev Plugins (${devPlugins.length}):`));
|
|
2022
|
+
for (const plugin of devPlugins) {
|
|
2023
|
+
const name = resolvePluginName(plugin);
|
|
2024
|
+
const version = resolvePluginVersion(plugin);
|
|
2025
|
+
console.log(
|
|
2026
|
+
` ${chalk13.yellow("\u25CF")} ${chalk13.white(name)}` + (version !== "-" ? chalk13.dim(` v${version}`) : "") + chalk13.dim(" [dev]")
|
|
2027
|
+
);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
console.log("");
|
|
2031
|
+
} catch (error) {
|
|
2032
|
+
printError(error.message || String(error));
|
|
2033
|
+
process.exit(1);
|
|
2034
|
+
}
|
|
2035
|
+
});
|
|
2036
|
+
var infoSubCommand = new Command12("info").description("Show detailed information about a plugin").argument("<name>", "Plugin name or package name").argument("[config]", "Configuration file path").action(async (name, configSource) => {
|
|
2037
|
+
try {
|
|
2038
|
+
const { config } = await loadConfig(configSource);
|
|
2039
|
+
const allPlugins = [
|
|
2040
|
+
...config.plugins || [],
|
|
2041
|
+
...config.devPlugins || []
|
|
2042
|
+
];
|
|
2043
|
+
const found = allPlugins.find((p) => {
|
|
2044
|
+
const pName = resolvePluginName(p);
|
|
2045
|
+
return pName === name || pName.includes(name);
|
|
2046
|
+
});
|
|
2047
|
+
if (!found) {
|
|
2048
|
+
printError(`Plugin '${name}' not found in configuration`);
|
|
2049
|
+
console.log("");
|
|
2050
|
+
console.log(chalk13.dim(" Available plugins:"));
|
|
2051
|
+
for (const p of allPlugins) {
|
|
2052
|
+
console.log(chalk13.dim(` - ${resolvePluginName(p)}`));
|
|
2053
|
+
}
|
|
2054
|
+
console.log("");
|
|
2055
|
+
process.exit(1);
|
|
2056
|
+
}
|
|
2057
|
+
printHeader(`Plugin: ${resolvePluginName(found)}`);
|
|
2058
|
+
printKV("Name", resolvePluginName(found));
|
|
2059
|
+
printKV("Version", resolvePluginVersion(found));
|
|
2060
|
+
printKV("Type", resolvePluginType(found));
|
|
2061
|
+
const isDev = (config.devPlugins || []).includes(found);
|
|
2062
|
+
printKV("Environment", isDev ? "development" : "production");
|
|
2063
|
+
if (found && typeof found === "object") {
|
|
2064
|
+
const p = found;
|
|
2065
|
+
if (typeof p.description === "string") {
|
|
2066
|
+
printKV("Description", p.description);
|
|
2067
|
+
}
|
|
2068
|
+
if (Array.isArray(p.dependencies) && p.dependencies.length > 0) {
|
|
2069
|
+
printKV("Dependencies", p.dependencies.join(", "));
|
|
2070
|
+
}
|
|
2071
|
+
if (typeof p.init === "function") {
|
|
2072
|
+
printInfo("This is a runtime plugin instance (has init function)");
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
if (typeof found === "string") {
|
|
2076
|
+
printInfo("This is a string reference (will be imported at runtime)");
|
|
2077
|
+
}
|
|
2078
|
+
console.log("");
|
|
2079
|
+
} catch (error) {
|
|
2080
|
+
printError(error.message || String(error));
|
|
2081
|
+
process.exit(1);
|
|
2082
|
+
}
|
|
2083
|
+
});
|
|
2084
|
+
var addCommand = new Command12("add").description("Add a plugin to objectstack.config.ts").argument("<package>", "Plugin package name (e.g. @objectstack/plugin-auth)").option("-d, --dev", "Add as a dev-only plugin").option("-c, --config <path>", "Configuration file path").action(async (packageName, options) => {
|
|
2085
|
+
try {
|
|
2086
|
+
const configPath = resolveConfigPath(options?.config);
|
|
2087
|
+
printHeader("Add Plugin");
|
|
2088
|
+
console.log(` ${chalk13.dim("Package:")} ${chalk13.white(packageName)}`);
|
|
2089
|
+
console.log(` ${chalk13.dim("Config:")} ${chalk13.white(path11.relative(process.cwd(), configPath))}`);
|
|
2090
|
+
console.log("");
|
|
2091
|
+
addPluginToConfig(configPath, packageName);
|
|
2092
|
+
printSuccess(`Added ${chalk13.cyan(packageName)} to config`);
|
|
2093
|
+
console.log("");
|
|
2094
|
+
console.log(chalk13.dim(" Next steps:"));
|
|
2095
|
+
console.log(chalk13.dim(` 1. Install the package: pnpm add ${packageName}`));
|
|
2096
|
+
console.log(chalk13.dim(" 2. Run: os validate"));
|
|
2097
|
+
console.log("");
|
|
2098
|
+
} catch (error) {
|
|
2099
|
+
printError(error.message || String(error));
|
|
2100
|
+
process.exit(1);
|
|
2101
|
+
}
|
|
2102
|
+
});
|
|
2103
|
+
var removeCommand = new Command12("remove").alias("rm").description("Remove a plugin from objectstack.config.ts").argument("<name>", "Plugin name or package name to remove").option("-c, --config <path>", "Configuration file path").action(async (pluginName, options) => {
|
|
2104
|
+
try {
|
|
2105
|
+
const configPath = resolveConfigPath(options?.config);
|
|
2106
|
+
printHeader("Remove Plugin");
|
|
2107
|
+
console.log(` ${chalk13.dim("Plugin:")} ${chalk13.white(pluginName)}`);
|
|
2108
|
+
console.log(` ${chalk13.dim("Config:")} ${chalk13.white(path11.relative(process.cwd(), configPath))}`);
|
|
2109
|
+
console.log("");
|
|
2110
|
+
removePluginFromConfig(configPath, pluginName);
|
|
2111
|
+
printSuccess(`Removed ${chalk13.cyan(pluginName)} from config`);
|
|
2112
|
+
console.log("");
|
|
2113
|
+
console.log(chalk13.dim(" Tip: Run `pnpm remove " + pluginName + "` to uninstall the package"));
|
|
2114
|
+
console.log("");
|
|
2115
|
+
} catch (error) {
|
|
2116
|
+
printError(error.message || String(error));
|
|
2117
|
+
process.exit(1);
|
|
2118
|
+
}
|
|
2119
|
+
});
|
|
2120
|
+
var pluginCommand = new Command12("plugin").description("Manage plugins (list, info, add, remove)").addCommand(listCommand).addCommand(infoSubCommand).addCommand(addCommand).addCommand(removeCommand);
|
|
2121
|
+
|
|
2122
|
+
// src/utils/plugin-commands.ts
|
|
2123
|
+
import chalk14 from "chalk";
|
|
2124
|
+
async function loadPluginCommands(program2) {
|
|
2125
|
+
let config;
|
|
2126
|
+
try {
|
|
2127
|
+
const loaded = await loadConfig();
|
|
2128
|
+
config = loaded.config;
|
|
2129
|
+
} catch {
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
const plugins = [
|
|
2133
|
+
...config.plugins || [],
|
|
2134
|
+
...config.devPlugins || []
|
|
2135
|
+
];
|
|
2136
|
+
const contributions = [];
|
|
2137
|
+
for (const plugin of plugins) {
|
|
2138
|
+
if (!plugin || typeof plugin !== "object") continue;
|
|
2139
|
+
const p = plugin;
|
|
2140
|
+
const manifest = p.manifest;
|
|
2141
|
+
const contributes = manifest?.contributes ?? p.contributes;
|
|
2142
|
+
if (!contributes) continue;
|
|
2143
|
+
const commands = contributes.commands;
|
|
2144
|
+
if (!Array.isArray(commands)) continue;
|
|
2145
|
+
const pluginName = resolvePluginName2(p);
|
|
2146
|
+
for (const cmd of commands) {
|
|
2147
|
+
if (!cmd || typeof cmd.name !== "string") continue;
|
|
2148
|
+
contributions.push({
|
|
2149
|
+
name: cmd.name,
|
|
2150
|
+
description: typeof cmd.description === "string" ? cmd.description : void 0,
|
|
2151
|
+
module: typeof cmd.module === "string" ? cmd.module : void 0,
|
|
2152
|
+
pluginName
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
if (contributions.length === 0) return;
|
|
2157
|
+
for (const contribution of contributions) {
|
|
2158
|
+
try {
|
|
2159
|
+
const commands = await importPluginCommands(contribution);
|
|
2160
|
+
for (const cmd of commands) {
|
|
2161
|
+
program2.addCommand(cmd);
|
|
2162
|
+
}
|
|
2163
|
+
} catch (error) {
|
|
2164
|
+
if (process.env.DEBUG) {
|
|
2165
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2166
|
+
console.error(
|
|
2167
|
+
chalk14.yellow(` \u26A0 Failed to load CLI command '${contribution.name}' from plugin '${contribution.pluginName}': ${message}`)
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
async function importPluginCommands(contribution) {
|
|
2174
|
+
const moduleId = contribution.module ? `${contribution.pluginName}/${contribution.module.replace(/^\.\//, "")}` : contribution.pluginName;
|
|
2175
|
+
const mod = await import(moduleId);
|
|
2176
|
+
if (Array.isArray(mod.commands)) {
|
|
2177
|
+
return mod.commands.filter(isCommandInstance);
|
|
2178
|
+
}
|
|
2179
|
+
const defaultExport = mod.default;
|
|
2180
|
+
if (defaultExport) {
|
|
2181
|
+
if (Array.isArray(defaultExport)) {
|
|
2182
|
+
return defaultExport.filter(isCommandInstance);
|
|
2183
|
+
}
|
|
2184
|
+
if (isCommandInstance(defaultExport)) {
|
|
2185
|
+
return [defaultExport];
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
const commands = [];
|
|
2189
|
+
for (const key of Object.keys(mod)) {
|
|
2190
|
+
if (isCommandInstance(mod[key])) {
|
|
2191
|
+
commands.push(mod[key]);
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
return commands;
|
|
2195
|
+
}
|
|
2196
|
+
function isCommandInstance(value) {
|
|
2197
|
+
if (value === null || typeof value !== "object") return false;
|
|
2198
|
+
const obj = value;
|
|
2199
|
+
return typeof obj.name === "function" && typeof obj.description === "function" && typeof obj.action === "function" && typeof obj.parse === "function";
|
|
2200
|
+
}
|
|
2201
|
+
function resolvePluginName2(plugin) {
|
|
2202
|
+
if (typeof plugin.name === "string") return plugin.name;
|
|
2203
|
+
const manifest = plugin.manifest;
|
|
2204
|
+
if (manifest && typeof manifest.name === "string") return manifest.name;
|
|
2205
|
+
if (plugin.constructor && plugin.constructor.name !== "Object") return plugin.constructor.name;
|
|
2206
|
+
return "unknown";
|
|
2207
|
+
}
|
|
2208
|
+
|
|
1877
2209
|
// src/bin.ts
|
|
1878
2210
|
var require2 = createRequire2(import.meta.url);
|
|
1879
2211
|
var pkg = require2("../package.json");
|
|
1880
2212
|
process.on("unhandledRejection", (err) => {
|
|
1881
|
-
console.error(
|
|
2213
|
+
console.error(chalk15.red(`
|
|
1882
2214
|
\u2717 Unhandled error: ${err?.message || err}`));
|
|
1883
2215
|
if (err?.stack && process.env.DEBUG) {
|
|
1884
|
-
console.error(
|
|
2216
|
+
console.error(chalk15.dim(err.stack));
|
|
1885
2217
|
}
|
|
1886
2218
|
process.exit(1);
|
|
1887
2219
|
});
|
|
1888
|
-
var program = new
|
|
2220
|
+
var program = new Command13();
|
|
1889
2221
|
program.name("objectstack").description("ObjectStack CLI \u2014 Build metadata-driven apps with the ObjectStack Protocol").version(pkg.version, "-v, --version").configureHelp({
|
|
1890
2222
|
sortSubcommands: false
|
|
1891
2223
|
}).addHelpText("before", `
|
|
1892
|
-
${
|
|
2224
|
+
${chalk15.bold.cyan("\u25C6 ObjectStack CLI")} ${chalk15.dim(`v${pkg.version}`)}
|
|
1893
2225
|
`).addHelpText("after", `
|
|
1894
|
-
${
|
|
1895
|
-
${
|
|
1896
|
-
${
|
|
1897
|
-
${
|
|
1898
|
-
${
|
|
1899
|
-
${
|
|
1900
|
-
${
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
${
|
|
2226
|
+
${chalk15.bold("Workflow:")}
|
|
2227
|
+
${chalk15.dim("$")} os init ${chalk15.dim("# Create a new project")}
|
|
2228
|
+
${chalk15.dim("$")} os generate object task ${chalk15.dim("# Add metadata")}
|
|
2229
|
+
${chalk15.dim("$")} os plugin add <package> ${chalk15.dim("# Add a plugin")}
|
|
2230
|
+
${chalk15.dim("$")} os validate ${chalk15.dim("# Check configuration")}
|
|
2231
|
+
${chalk15.dim("$")} os dev ${chalk15.dim("# Start dev server")}
|
|
2232
|
+
${chalk15.dim("$")} os studio ${chalk15.dim("# Dev server + Studio UI")}
|
|
2233
|
+
${chalk15.dim("$")} os compile ${chalk15.dim("# Build for production")}
|
|
2234
|
+
|
|
2235
|
+
${chalk15.dim("Aliases: objectstack | os")}
|
|
2236
|
+
${chalk15.dim("Docs: https://objectstack.dev")}
|
|
1904
2237
|
`);
|
|
1905
2238
|
program.addCommand(initCommand);
|
|
1906
2239
|
program.addCommand(devCommand);
|
|
@@ -1911,6 +2244,15 @@ program.addCommand(validateCommand);
|
|
|
1911
2244
|
program.addCommand(infoCommand);
|
|
1912
2245
|
program.addCommand(generateCommand);
|
|
1913
2246
|
program.addCommand(createCommand);
|
|
2247
|
+
program.addCommand(pluginCommand);
|
|
1914
2248
|
program.addCommand(testCommand);
|
|
1915
2249
|
program.addCommand(doctorCommand);
|
|
1916
|
-
program.
|
|
2250
|
+
loadPluginCommands(program).then(() => {
|
|
2251
|
+
program.parse(process.argv);
|
|
2252
|
+
}).catch((err) => {
|
|
2253
|
+
if (process.env.DEBUG) {
|
|
2254
|
+
console.error(chalk15.yellow(`
|
|
2255
|
+
\u26A0 Plugin command loading failed: ${err?.message || err}`));
|
|
2256
|
+
}
|
|
2257
|
+
program.parse(process.argv);
|
|
2258
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -90,6 +90,8 @@ declare const templates: {
|
|
|
90
90
|
};
|
|
91
91
|
declare const createCommand: Command;
|
|
92
92
|
|
|
93
|
+
declare const pluginCommand: Command;
|
|
94
|
+
|
|
93
95
|
declare const devCommand: Command;
|
|
94
96
|
|
|
95
97
|
declare const serveCommand: Command;
|
|
@@ -98,4 +100,15 @@ declare const testCommand: Command;
|
|
|
98
100
|
|
|
99
101
|
declare const doctorCommand: Command;
|
|
100
102
|
|
|
101
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Discover CLI command contributions from installed plugins.
|
|
105
|
+
*
|
|
106
|
+
* Scans the project's `objectstack.config.ts` for plugins that declare
|
|
107
|
+
* `contributes.commands` in their manifest, then dynamically imports
|
|
108
|
+
* those plugin modules to register Commander.js commands.
|
|
109
|
+
*
|
|
110
|
+
* @param program - The root Commander.js program to register commands on
|
|
111
|
+
*/
|
|
112
|
+
declare function loadPluginCommands(program: Command): Promise<void>;
|
|
113
|
+
|
|
114
|
+
export { compileCommand, createCommand, devCommand, doctorCommand, generateCommand, infoCommand, initCommand, loadPluginCommands, pluginCommand, serveCommand, templates, testCommand, validateCommand };
|