@ollie-shop/cli 0.3.3 → 1.0.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/.turbo/turbo-build.log +6 -9
- package/CHANGELOG.md +27 -0
- package/dist/index.js +1003 -40565
- package/package.json +15 -37
- package/src/README.md +126 -0
- package/src/cli.tsx +45 -0
- package/src/commands/help.tsx +79 -0
- package/src/commands/login.tsx +92 -0
- package/src/commands/start.tsx +411 -0
- package/src/index.tsx +8 -0
- package/src/utils/auth.ts +218 -21
- package/src/utils/bundle.ts +177 -0
- package/src/utils/config.ts +123 -0
- package/src/utils/esbuild.ts +533 -0
- package/tsconfig.json +10 -15
- package/tsup.config.ts +8 -10
- package/CLAUDE_CLI.md +0 -265
- package/README.md +0 -711
- package/__tests__/mocks/console.ts +0 -22
- package/__tests__/mocks/core.ts +0 -137
- package/__tests__/mocks/index.ts +0 -4
- package/__tests__/mocks/inquirer.ts +0 -16
- package/__tests__/mocks/progress.ts +0 -19
- package/dist/index.d.ts +0 -1
- package/src/__tests__/helpers/cli-test-helper.ts +0 -281
- package/src/__tests__/mocks/index.ts +0 -142
- package/src/actions/component.actions.ts +0 -278
- package/src/actions/function.actions.ts +0 -220
- package/src/actions/project.actions.ts +0 -131
- package/src/actions/version.actions.ts +0 -233
- package/src/commands/__tests__/component-validation.test.ts +0 -250
- package/src/commands/__tests__/component.test.ts +0 -318
- package/src/commands/__tests__/function-validation.test.ts +0 -220
- package/src/commands/__tests__/function.test.ts +0 -286
- package/src/commands/__tests__/store-version-validation.test.ts +0 -414
- package/src/commands/__tests__/store-version.test.ts +0 -402
- package/src/commands/component.ts +0 -178
- package/src/commands/docs.ts +0 -24
- package/src/commands/function.ts +0 -201
- package/src/commands/help.ts +0 -18
- package/src/commands/index.ts +0 -27
- package/src/commands/login.ts +0 -267
- package/src/commands/project.ts +0 -107
- package/src/commands/store-version.ts +0 -242
- package/src/commands/version.ts +0 -51
- package/src/commands/whoami.ts +0 -46
- package/src/index.ts +0 -116
- package/src/prompts/component.prompts.ts +0 -94
- package/src/prompts/function.prompts.ts +0 -168
- package/src/schemas/command.schema.ts +0 -644
- package/src/types/index.ts +0 -183
- package/src/utils/__tests__/command-parser.test.ts +0 -159
- package/src/utils/__tests__/command-suggestions.test.ts +0 -185
- package/src/utils/__tests__/console.test.ts +0 -192
- package/src/utils/__tests__/context-detector.test.ts +0 -258
- package/src/utils/__tests__/enhanced-error-handler.test.ts +0 -137
- package/src/utils/__tests__/error-handler.test.ts +0 -107
- package/src/utils/__tests__/rich-progress.test.ts +0 -181
- package/src/utils/__tests__/validation-error-formatter.test.ts +0 -175
- package/src/utils/__tests__/validation-helpers.test.ts +0 -125
- package/src/utils/cli-progress-reporter.ts +0 -84
- package/src/utils/command-builder.ts +0 -390
- package/src/utils/command-helpers.ts +0 -83
- package/src/utils/command-parser.ts +0 -245
- package/src/utils/command-suggestions.ts +0 -176
- package/src/utils/console.ts +0 -320
- package/src/utils/constants.ts +0 -39
- package/src/utils/context-detector.ts +0 -177
- package/src/utils/deploy-helpers.ts +0 -357
- package/src/utils/enhanced-error-handler.ts +0 -264
- package/src/utils/error-handler.ts +0 -60
- package/src/utils/errors.ts +0 -256
- package/src/utils/interactive-builder.ts +0 -325
- package/src/utils/rich-progress.ts +0 -331
- package/src/utils/store.ts +0 -23
- package/src/utils/validation-error-formatter.ts +0 -337
- package/src/utils/validation-helpers.ts +0 -325
- package/vitest.config.ts +0 -35
- package/vitest.setup.ts +0 -29
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { PassThrough } from "node:stream";
|
|
4
|
+
import archiver from "archiver";
|
|
5
|
+
|
|
6
|
+
// TODO: Implement .ollieignore file support for custom exclusions
|
|
7
|
+
|
|
8
|
+
export interface BundleOptions {
|
|
9
|
+
/** Project root directory */
|
|
10
|
+
cwd?: string;
|
|
11
|
+
/** Component name (folder name in components/) */
|
|
12
|
+
componentName: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface BundleResult {
|
|
16
|
+
/** Readable stream of the zip file */
|
|
17
|
+
stream: PassThrough;
|
|
18
|
+
/** Size of the zip in bytes (available after stream ends) */
|
|
19
|
+
size: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates a zip bundle for a component with a synthetic entry point.
|
|
24
|
+
*
|
|
25
|
+
* The bundle includes:
|
|
26
|
+
* - index.tsx: synthetic entry point that re-exports the component
|
|
27
|
+
* - package.json: project dependencies
|
|
28
|
+
* - components/: all component source files
|
|
29
|
+
* - Any other user directories (utils/, hooks/, etc.)
|
|
30
|
+
*
|
|
31
|
+
* Excludes: node_modules, .git, .next, dist, build, etc.
|
|
32
|
+
*/
|
|
33
|
+
export async function createComponentBundle(
|
|
34
|
+
options: BundleOptions,
|
|
35
|
+
): Promise<PassThrough> {
|
|
36
|
+
const { cwd = process.cwd(), componentName } = options;
|
|
37
|
+
|
|
38
|
+
// Verify component exists
|
|
39
|
+
const componentDir = path.join(cwd, "components", componentName);
|
|
40
|
+
try {
|
|
41
|
+
await fs.access(componentDir);
|
|
42
|
+
} catch {
|
|
43
|
+
throw new Error(`Component "${componentName}" not found in components/`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Verify package.json exists
|
|
47
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
48
|
+
try {
|
|
49
|
+
await fs.access(packageJsonPath);
|
|
50
|
+
} catch {
|
|
51
|
+
throw new Error("package.json not found in project root");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Create archive with max compression
|
|
55
|
+
const archive = archiver("zip", {
|
|
56
|
+
zlib: { level: 9 },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Create output stream
|
|
60
|
+
const output = new PassThrough();
|
|
61
|
+
|
|
62
|
+
archive.pipe(output);
|
|
63
|
+
|
|
64
|
+
// Handle archive errors
|
|
65
|
+
archive.on("error", (err) => {
|
|
66
|
+
output.destroy(err);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Add synthetic entry point
|
|
70
|
+
const entryPoint = `export { default } from './components/${componentName}';\n`;
|
|
71
|
+
archive.append(entryPoint, { name: "index.tsx" });
|
|
72
|
+
|
|
73
|
+
// Add package.json
|
|
74
|
+
archive.file(packageJsonPath, { name: "package.json" });
|
|
75
|
+
|
|
76
|
+
// Add all directories from project root (excluding defaults)
|
|
77
|
+
const entries = await fs.readdir(cwd, { withFileTypes: true });
|
|
78
|
+
|
|
79
|
+
for (const entry of entries) {
|
|
80
|
+
// Skip excluded patterns
|
|
81
|
+
if (shouldExclude(entry.name)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const entryPath = path.join(cwd, entry.name);
|
|
86
|
+
|
|
87
|
+
if (entry.isDirectory()) {
|
|
88
|
+
// Add directory recursively
|
|
89
|
+
archive.directory(entryPath, entry.name, (data) => {
|
|
90
|
+
// Filter out excluded patterns within directories
|
|
91
|
+
const relativePath = path.relative(cwd, data.name || "");
|
|
92
|
+
if (shouldExcludeFile(relativePath)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
return data;
|
|
96
|
+
});
|
|
97
|
+
} else if (entry.isFile() && isSourceFile(entry.name)) {
|
|
98
|
+
// Add root-level source files (but not package.json, already added)
|
|
99
|
+
if (entry.name !== "package.json") {
|
|
100
|
+
archive.file(entryPath, { name: entry.name });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Finalize the archive
|
|
106
|
+
archive.finalize();
|
|
107
|
+
|
|
108
|
+
return output;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Check if a file/directory should be excluded at the root level
|
|
113
|
+
*/
|
|
114
|
+
function shouldExclude(name: string): boolean {
|
|
115
|
+
const excludeRoots = [
|
|
116
|
+
"node_modules",
|
|
117
|
+
".git",
|
|
118
|
+
".next",
|
|
119
|
+
"dist",
|
|
120
|
+
"build",
|
|
121
|
+
".turbo",
|
|
122
|
+
".cache",
|
|
123
|
+
"coverage",
|
|
124
|
+
];
|
|
125
|
+
return excludeRoots.includes(name) || name.startsWith(".");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Check if a file path should be excluded
|
|
130
|
+
*/
|
|
131
|
+
function shouldExcludeFile(relativePath: string): boolean {
|
|
132
|
+
// Exclude node_modules anywhere in the tree
|
|
133
|
+
if (relativePath.includes("node_modules")) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Exclude common build artifacts
|
|
138
|
+
const excludePatterns = [
|
|
139
|
+
/\.tsbuildinfo$/,
|
|
140
|
+
/\.d\.ts$/,
|
|
141
|
+
/\.js\.map$/,
|
|
142
|
+
/\.next\//,
|
|
143
|
+
/dist\//,
|
|
144
|
+
/build\//,
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
return excludePatterns.some((pattern) => pattern.test(relativePath));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Check if a file is a source file we want to include
|
|
152
|
+
*/
|
|
153
|
+
function isSourceFile(name: string): boolean {
|
|
154
|
+
const sourceExtensions = [
|
|
155
|
+
".ts",
|
|
156
|
+
".tsx",
|
|
157
|
+
".js",
|
|
158
|
+
".jsx",
|
|
159
|
+
".json",
|
|
160
|
+
".css",
|
|
161
|
+
".scss",
|
|
162
|
+
".less",
|
|
163
|
+
".svg",
|
|
164
|
+
".png",
|
|
165
|
+
".jpg",
|
|
166
|
+
".jpeg",
|
|
167
|
+
".gif",
|
|
168
|
+
".webp",
|
|
169
|
+
".ico",
|
|
170
|
+
".woff",
|
|
171
|
+
".woff2",
|
|
172
|
+
".ttf",
|
|
173
|
+
".eot",
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
return sourceExtensions.some((ext) => name.endsWith(ext));
|
|
177
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
const OllieConfigSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
storeId: z.string().uuid(),
|
|
8
|
+
versionId: z.string().uuid().optional(),
|
|
9
|
+
})
|
|
10
|
+
.passthrough(); // Allow any other fields without validation
|
|
11
|
+
|
|
12
|
+
export type OllieConfig = z.infer<typeof OllieConfigSchema>;
|
|
13
|
+
|
|
14
|
+
const CONFIG_FILE = "ollie.json";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Gets the config file name for a given stage.
|
|
18
|
+
* - No stage or "prod" → ollie.json
|
|
19
|
+
* - Other stages → ollie.{stage}.json
|
|
20
|
+
*/
|
|
21
|
+
function getConfigFileName(stage?: string): string {
|
|
22
|
+
if (!stage || stage === "prod") {
|
|
23
|
+
return CONFIG_FILE;
|
|
24
|
+
}
|
|
25
|
+
return `ollie.${stage}.json`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Loads a single config file (without stage resolution).
|
|
30
|
+
*/
|
|
31
|
+
async function loadConfigFile(
|
|
32
|
+
filePath: string,
|
|
33
|
+
): Promise<Record<string, unknown> | null> {
|
|
34
|
+
try {
|
|
35
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
36
|
+
return JSON.parse(content);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface LoadConfigOptions {
|
|
46
|
+
cwd?: string;
|
|
47
|
+
stage?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Loads the config for a given stage.
|
|
52
|
+
* - Loads ollie.json as base config
|
|
53
|
+
* - If stage is provided (and not "prod"), merges ollie.{stage}.json on top
|
|
54
|
+
*/
|
|
55
|
+
export async function loadConfig(
|
|
56
|
+
options: LoadConfigOptions = {},
|
|
57
|
+
): Promise<OllieConfig | null> {
|
|
58
|
+
const { cwd = process.cwd(), stage } = options;
|
|
59
|
+
|
|
60
|
+
// Load base config (ollie.json)
|
|
61
|
+
const basePath = path.join(cwd, CONFIG_FILE);
|
|
62
|
+
const baseConfig = await loadConfigFile(basePath);
|
|
63
|
+
|
|
64
|
+
if (!baseConfig) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// If no stage or stage is "prod", return base config
|
|
69
|
+
if (!stage || stage === "prod") {
|
|
70
|
+
return OllieConfigSchema.parse(baseConfig);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Load stage-specific config
|
|
74
|
+
const stageFileName = getConfigFileName(stage);
|
|
75
|
+
const stagePath = path.join(cwd, stageFileName);
|
|
76
|
+
const stageConfig = await loadConfigFile(stagePath);
|
|
77
|
+
|
|
78
|
+
if (!stageConfig) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Stage config not found: ${stageFileName}. Create it or use --stage prod (default).`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Merge: stage config overrides base config
|
|
85
|
+
const merged = {
|
|
86
|
+
...baseConfig,
|
|
87
|
+
...stageConfig,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return OllieConfigSchema.parse(merged);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface SaveConfigOptions {
|
|
94
|
+
cwd?: string;
|
|
95
|
+
stage?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function saveConfig(
|
|
99
|
+
config: Partial<OllieConfig>,
|
|
100
|
+
options: SaveConfigOptions = {},
|
|
101
|
+
): Promise<void> {
|
|
102
|
+
const { cwd = process.cwd(), stage } = options;
|
|
103
|
+
const fileName = getConfigFileName(stage);
|
|
104
|
+
const configPath = path.join(cwd, fileName);
|
|
105
|
+
|
|
106
|
+
// Load existing config for this specific file
|
|
107
|
+
const existing = await loadConfigFile(configPath);
|
|
108
|
+
|
|
109
|
+
const merged = {
|
|
110
|
+
...existing,
|
|
111
|
+
...config,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
await fs.writeFile(configPath, JSON.stringify(merged, null, 2));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Returns the resolved stage from CLI args or environment.
|
|
119
|
+
* Priority: CLI arg > OLLIE_STAGE env > undefined (defaults to prod behavior)
|
|
120
|
+
*/
|
|
121
|
+
export function resolveStage(cliStage?: string): string | undefined {
|
|
122
|
+
return cliStage || process.env.OLLIE_STAGE || undefined;
|
|
123
|
+
}
|