create-ollie-shop 0.1.1

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.
@@ -0,0 +1,13 @@
1
+
2
+ > create-ollie-shop@0.1.1 build /home/runner/work/ollie-shop/ollie-shop/packages/create-ollie-shop
3
+ > tsup
4
+
5
+ CLI Building entry: src/index.ts
6
+ CLI Using tsconfig: tsconfig.json
7
+ CLI tsup v8.5.0
8
+ CLI Using tsup config: /home/runner/work/ollie-shop/ollie-shop/packages/create-ollie-shop/tsup.config.ts
9
+ CLI Target: es2022
10
+ CLI Cleaning output folder
11
+ ESM Build start
12
+ ESM dist/index.js 6.28 KB
13
+ ESM ⚡️ Build success in 90ms
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # create-ollie-shop
2
+
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - b6c3baf: Initial release
8
+ - Updated dependencies [b6c3baf]
9
+ - @ollie-shop/previewjs@0.1.1
package/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # create-ollie-shop
2
+
3
+ A CLI tool to create a new Ollie Shop e-commerce project with a modern tech stack and pre-built components.
4
+
5
+ ## Overview
6
+
7
+ `create-ollie-shop` is a scaffolding tool that helps you quickly set up a new e-commerce project using the Ollie Shop framework. It provides a selection of templates, components, and configurations to kickstart your online store development.
8
+
9
+ ## Requirements
10
+
11
+ - Node.js 20.x or higher
12
+ - pnpm 7.x or higher (recommended package manager)
13
+
14
+ ## Installation
15
+
16
+ ### Using pnpm create
17
+
18
+ ```bash
19
+ pnpm create ollie-shop [project-name]
20
+ ```
21
+
22
+ ### Or use directly with npx
23
+
24
+ ```bash
25
+ npx create-ollie-shop [project-name]
26
+ ```
27
+
28
+ ### Options
29
+
30
+ - `--store-id <id>` - Specify an Ollie store ID
31
+ - `--version-id <id>` - Specify an Ollie store version ID
32
+ - `--help` - Display help information
33
+ - `--version` - Display version information
34
+
35
+ ### Examples
36
+
37
+ ```bash
38
+ # Start interactive project bootstrapping
39
+ npx create-ollie-shop
40
+
41
+ # Create a new project with a custom name
42
+ npx create-ollie-shop my-awesome-store
43
+
44
+ # Create a new project with store configuration
45
+ npx create-ollie-shop my-store --store-id 7217542a-d7c6-40d3-a20e-db13b310a781 --version-id 882c8c18-550c-4ac5-9d3b-015d7dd25741
46
+ ```
47
+
48
+ ## Preview.js Plugin
49
+
50
+ Ollie Shop projects were designed to be previewed using the Preview.js plugin for VS Code. This tool allows developers to visualize and interact with components in isolation during development.
51
+
52
+ ### Installation
53
+
54
+ Simply install the Preview.js extension from the VS Code marketplace:
55
+ [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=zenclabs.previewjs)
56
+
57
+ ### Usage
58
+
59
+ Preview.js works seamlessly with properly bootstrapped Ollie Shop projects. After creating your project with `create-ollie-shop`, the Preview.js server will automatically read the Ollie configurations and set up accordingly.
60
+
61
+ To use Preview.js:
62
+ 1. Open any component file in your Ollie Shop project
63
+ 2. Click the Preview.js icon in the editor or press the keyboard shortcut
64
+ 3. A live preview of your component will appear, reflecting any changes you make in real-time
65
+
66
+ This integration allows you to develop and test components without needing to set up a separate development environment or manually configure Preview.js.
67
+
68
+ ## Development
69
+
70
+ To work on `create-ollie-shop` locally:
71
+
72
+ 1. Clone the repository
73
+
74
+ ```bash
75
+ git clone git@github.com:ollie-shop/ollie-shop.git
76
+ cd ollie-shop
77
+ ```
78
+
79
+ 2. Install dependencies
80
+
81
+ ```bash
82
+ pnpm install
83
+ ```
84
+
85
+ 3. Link local packages
86
+
87
+ ```bash
88
+ # Navigate to the create-ollie-shop package directory
89
+ cd packages/create-ollie-shop
90
+
91
+ # Create a global link
92
+ pnpm link --global
93
+
94
+ # For working with other local packages, use pnpm link inside each package directory
95
+ # For example, to link the ollie-shop library:
96
+ cd ../ollie-shop
97
+ pnpm link --global
98
+
99
+ # Then, in the create-ollie-shop directory:
100
+ pnpm link --global ollie-shop
101
+ ```
102
+
103
+ > **Note**: When using `pnpm link`, make sure all linked packages have their dependencies installed. The link creates a symlink to your local package, allowing you to make changes and immediately see them reflected in projects that use the linked package.
104
+
105
+ 4. Build the package
106
+
107
+ ```bash
108
+ pnpm build
109
+ ```
110
+
111
+ 5. Test your changes
112
+
113
+ ```bash
114
+ pnpm test
115
+ ```
116
+
117
+ ## Contributing
118
+
119
+ Contributions are welcome! Please feel free to submit a Pull Request.
120
+
121
+ 1. Fork the repository
122
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
123
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
124
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
125
+ 5. Open a Pull Request
126
+
127
+ ## License
128
+
129
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import path2 from "path";
5
+ import chalk3 from "chalk";
6
+ import { Command } from "commander";
7
+ import fs2 from "fs-extra";
8
+
9
+ // src/commands/create-project.ts
10
+ import child_process from "child_process";
11
+ import path from "path";
12
+ import { fileURLToPath } from "url";
13
+ import { promisify } from "util";
14
+ import chalk from "chalk";
15
+ import fs from "fs-extra";
16
+ var exec = promisify(child_process.exec);
17
+ var BASE_DIR = path.dirname(fileURLToPath(import.meta.url));
18
+ async function createProject(config) {
19
+ const {
20
+ projectName,
21
+ storeId,
22
+ versionId,
23
+ platform,
24
+ platformStoreId,
25
+ targetDir
26
+ } = config;
27
+ console.log(
28
+ `
29
+ \u{1F680} Creating a new Ollie Shop project in ${chalk.green(`./${projectName}`)}...`
30
+ );
31
+ fs.ensureDirSync(targetDir);
32
+ const templateDir = path.join(BASE_DIR, "template");
33
+ await fs.copy(templateDir, targetDir, {
34
+ overwrite: true
35
+ });
36
+ const pkgPath = path.join(targetDir, "package.json");
37
+ const pkg = await fs.readJSON(path.join(targetDir, "package.json"));
38
+ pkg.name = projectName;
39
+ await fs.writeJSON(pkgPath, pkg, { spaces: 2 });
40
+ const ollieConfigPath = path.join(targetDir, "ollie.json");
41
+ await fs.writeJSON(
42
+ ollieConfigPath,
43
+ {
44
+ storeId: storeId || null,
45
+ versionId: versionId || null,
46
+ platform,
47
+ platformStoreId: platformStoreId || null
48
+ },
49
+ { spaces: 2 }
50
+ );
51
+ try {
52
+ await exec("git init", { cwd: targetDir });
53
+ } catch (e) {
54
+ console.log("\n\u274C Error initializing git:", e);
55
+ }
56
+ printSuccessMessage(
57
+ projectName,
58
+ storeId,
59
+ versionId,
60
+ platform,
61
+ platformStoreId
62
+ );
63
+ }
64
+ function printSuccessMessage(projectName, storeId, versionId, platform, platformStoreId) {
65
+ console.log("\n\u2705 Project created successfully!");
66
+ console.log("\nNext steps:");
67
+ console.log(` cd ${chalk.cyan(projectName)}`);
68
+ console.log(
69
+ ` npm install (or ${chalk.cyan("pnpm install")} or ${chalk.cyan("yarn")})`
70
+ );
71
+ console.log(
72
+ "\n The development environment leverages the Preview.js extension for VS Code:"
73
+ );
74
+ console.log(
75
+ ` ${chalk.cyan("https://marketplace.visualstudio.com/items?itemName=zenclabs.previewjs")}
76
+ `
77
+ );
78
+ if (storeId) {
79
+ console.log(`Store ID: ${chalk.green(storeId)}`);
80
+ } else if (platform && platformStoreId) {
81
+ console.log(`Platform: ${chalk.green(platform)}`);
82
+ console.log(`Platform Store ID: ${chalk.green(platformStoreId)}`);
83
+ }
84
+ if (versionId) {
85
+ console.log(`Version ID: ${chalk.green(versionId)}`);
86
+ }
87
+ console.log("\n\u{1F6CD}\uFE0F Happy development with Ollie Shop! \u{1F6CD}\uFE0F\n");
88
+ }
89
+
90
+ // src/utils/prompts.ts
91
+ import chalk2 from "chalk";
92
+ import prompts from "prompts";
93
+ import { z } from "zod";
94
+ async function promptForProjectName(defaultValue = "my-ollie-shop") {
95
+ const { value } = await prompts({
96
+ type: "text",
97
+ name: "value",
98
+ message: "Project name:",
99
+ initial: defaultValue,
100
+ validate: (value2) => z.string().nonempty().safeParse(value2).success
101
+ });
102
+ return value || defaultValue;
103
+ }
104
+ async function promptForOverwrite(dirName) {
105
+ const { overwrite } = await prompts({
106
+ type: "confirm",
107
+ name: "overwrite",
108
+ message: `Directory ${chalk2.cyan(dirName)} already exists. Overwrite?`,
109
+ initial: false
110
+ });
111
+ return overwrite;
112
+ }
113
+ async function promptForStoreId() {
114
+ const { value } = await prompts({
115
+ type: "text",
116
+ name: "value",
117
+ message: "Store ID (optional):",
118
+ hint: "Leave empty if you want to set up the workspace manually",
119
+ validate: (value2) => z.string().uuid().or(z.literal("")).safeParse(value2).success || "Please enter a valid UUID or leave empty"
120
+ });
121
+ return value && value.trim() !== "" ? value : void 0;
122
+ }
123
+ async function promptForPlatformStoreId(platform) {
124
+ const { platformStoreId } = await prompts({
125
+ type: "text",
126
+ name: "platformStoreId",
127
+ message: `Enter your ${platform.toUpperCase()} account name:`,
128
+ validate: (value) => z.string().min(1).safeParse(value).success || "Account name cannot be empty"
129
+ });
130
+ return platformStoreId;
131
+ }
132
+ async function promptForVersionId() {
133
+ const { value } = await prompts({
134
+ type: "text",
135
+ name: "value",
136
+ message: "Version ID (optional):",
137
+ validate: (value2) => z.string().uuid().or(z.literal("")).safeParse(value2).success
138
+ });
139
+ return value && value.trim() !== "" ? value : void 0;
140
+ }
141
+
142
+ // src/index.ts
143
+ async function main() {
144
+ console.log(
145
+ chalk3.bold.blue(
146
+ "\u{1F6CD}\uFE0F Create Ollie Shop - Bootstrap a new Ollie Shop project"
147
+ )
148
+ );
149
+ const program = new Command().name("create-ollie-shop").description(
150
+ "Bootstrap a new Ollie Shop project with optional store configuration"
151
+ ).version("0.1.0").argument("[project-name]", "Project folder name", "my-ollie-shop").option("--store-id <id>", "Specify an Ollie store ID").option("--version-id <id>", "Specify an Ollie store version ID").addHelpText(
152
+ "after",
153
+ `
154
+ Examples:
155
+ $ create-ollie-shop
156
+ $ create-ollie-shop my-awesome-store
157
+ $ create-ollie-shop my-store --store-id 7217542a-d7c6-40d3-a20e-db13b310a781 --version-id 882c8c18-550c-4ac5-9d3b-015d7dd25741
158
+ `
159
+ ).parse(process.argv);
160
+ try {
161
+ await initializeProject(program);
162
+ } catch (error) {
163
+ console.error(chalk3.red("\n\u274C Error:"), error);
164
+ process.exit(1);
165
+ }
166
+ }
167
+ async function initializeProject(program) {
168
+ const options = program.opts();
169
+ let storeId = options.storeId;
170
+ let versionId = options.versionId;
171
+ const platform = "vtex";
172
+ let platformStoreId;
173
+ let projectName = program.args[0];
174
+ if (!projectName) {
175
+ projectName = await promptForProjectName();
176
+ }
177
+ const cwd = process.cwd();
178
+ const targetDir = path2.join(cwd, projectName);
179
+ if (fs2.existsSync(targetDir)) {
180
+ const shouldOverwrite = await promptForOverwrite(projectName);
181
+ if (!shouldOverwrite) {
182
+ console.log(chalk3.red("\u274C Operation cancelled"));
183
+ return;
184
+ }
185
+ await fs2.emptyDir(targetDir);
186
+ }
187
+ if (!storeId) {
188
+ storeId = await promptForStoreId();
189
+ if (!storeId) {
190
+ console.log(
191
+ chalk3.blue(
192
+ `Using ${chalk3.bold(platform.toUpperCase())} as the platform`
193
+ )
194
+ );
195
+ platformStoreId = await promptForPlatformStoreId(platform);
196
+ }
197
+ }
198
+ if (!versionId) {
199
+ versionId = await promptForVersionId();
200
+ }
201
+ const config = {
202
+ projectName,
203
+ storeId,
204
+ versionId,
205
+ platform,
206
+ platformStoreId,
207
+ targetDir
208
+ };
209
+ await createProject(config);
210
+ }
211
+ main();
@@ -0,0 +1,3 @@
1
+ {
2
+ "recommendations": ["biomejs.biome", "zenclabs.previewjs"]
3
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "editor.formatOnSave": true,
3
+ "editor.defaultFormatter": "biomejs.biome",
4
+ "editor.codeActionsOnSave": {
5
+ "source.fixAll.biome": "explicit",
6
+ "source.organizeImports.biome": "explicit"
7
+ },
8
+ "[typescript]": {
9
+ "editor.defaultFormatter": "biomejs.biome"
10
+ },
11
+ "[typescriptreact]": {
12
+ "editor.defaultFormatter": "biomejs.biome"
13
+ },
14
+ "[javascript]": {
15
+ "editor.defaultFormatter": "biomejs.biome"
16
+ },
17
+ "[javascriptreact]": {
18
+ "editor.defaultFormatter": "biomejs.biome"
19
+ },
20
+ "[json]": {
21
+ "editor.defaultFormatter": "biomejs.biome"
22
+ },
23
+ "[jsonc]": {
24
+ "editor.defaultFormatter": "biomejs.biome"
25
+ }
26
+ }
@@ -0,0 +1,10 @@
1
+ import styles from "./styles.module.css";
2
+
3
+ export default function HelloWorld({ name = "Stranger" }: { name?: string }) {
4
+ return (
5
+ <div className={styles.container}>
6
+ <h1>Hello, {name}!</h1>
7
+ <p>Test</p>
8
+ </div>
9
+ );
10
+ }
@@ -0,0 +1,7 @@
1
+ .container {
2
+ display: flex;
3
+ justify-content: center;
4
+ align-items: center;
5
+ flex-direction: column;
6
+ gap: 1rem;
7
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "dev",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "devDependencies": {
6
+ "@ollie-shop/sdk": "^0.1.0",
7
+ "@ollie-shop/previewjs": "^0.1.0",
8
+ "@types/node": "^22.14.0",
9
+ "@types/react": "^19.1.0",
10
+ "@types/react-dom": "^19.1.1",
11
+ "next": "^15.3.1",
12
+ "react": "^19.0.0",
13
+ "react-dom": "^19.0.0",
14
+ "typescript": "^5.7.3",
15
+ "vite": "^6.2.5"
16
+ }
17
+ }
@@ -0,0 +1,3 @@
1
+ import { defineConfig } from "@ollie-shop/previewjs";
2
+
3
+ export default defineConfig();
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["ES2022"],
4
+ "module": "ES2022",
5
+ "target": "ES2022",
6
+ "moduleResolution": "Bundler",
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "declaration": true,
10
+ "noUncheckedIndexedAccess": true,
11
+ "skipLibCheck": true,
12
+ "esModuleInterop": true,
13
+ "resolveJsonModule": true,
14
+ "typeRoots": ["./node_modules/@types", "./types"],
15
+ "types": ["node"]
16
+ },
17
+ "exclude": ["node_modules", "dist"]
18
+ }
@@ -0,0 +1,4 @@
1
+ declare module "*.css" {
2
+ const classes: { readonly [key: string]: string };
3
+ export default classes;
4
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "create-ollie-shop",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "create-ollie-shop": "./dist/index.js"
9
+ },
10
+ "devDependencies": {
11
+ "@types/fs-extra": "^11.0.4",
12
+ "@types/node": "^22.14.0",
13
+ "@types/prompts": "^2.4.9",
14
+ "tsup": "^8.4.0",
15
+ "typescript": "^5.7.3",
16
+ "vitest": "^3.0.4"
17
+ },
18
+ "dependencies": {
19
+ "chalk": "^5.4.1",
20
+ "commander": "^11.1.0",
21
+ "fs-extra": "^11.3.0",
22
+ "prompts": "^2.4.2",
23
+ "zod": "^3.24.2",
24
+ "@ollie-shop/previewjs": "^0.1.1"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public",
28
+ "registry": "https://registry.npmjs.org/"
29
+ },
30
+ "scripts": {
31
+ "build": "tsup",
32
+ "test": "vitest"
33
+ }
34
+ }
@@ -0,0 +1,114 @@
1
+ import child_process from "node:child_process";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { promisify } from "node:util";
5
+ import chalk from "chalk";
6
+ import fs from "fs-extra";
7
+ import type { OllieShopConfig } from "../types";
8
+
9
+ const exec = promisify(child_process.exec);
10
+
11
+ const BASE_DIR = path.dirname(fileURLToPath(import.meta.url));
12
+ /**
13
+ * Create a new Ollie Shop project
14
+ */
15
+ export async function createProject(config: OllieShopConfig): Promise<void> {
16
+ const {
17
+ projectName,
18
+ storeId,
19
+ versionId,
20
+ platform,
21
+ platformStoreId,
22
+ targetDir,
23
+ } = config;
24
+
25
+ console.log(
26
+ `\n🚀 Creating a new Ollie Shop project in ${chalk.green(`./${projectName}`)}...`,
27
+ );
28
+
29
+ // Ensure target directory exists
30
+ fs.ensureDirSync(targetDir);
31
+
32
+ // Get template directory path
33
+ const templateDir = path.join(BASE_DIR, "template");
34
+
35
+ // Copy template files to target directory
36
+ await fs.copy(templateDir, targetDir, {
37
+ overwrite: true,
38
+ });
39
+
40
+ // Update package.json with project name
41
+ const pkgPath = path.join(targetDir, "package.json");
42
+ const pkg = await fs.readJSON(path.join(targetDir, "package.json"));
43
+ pkg.name = projectName;
44
+ await fs.writeJSON(pkgPath, pkg, { spaces: 2 });
45
+
46
+ // Update ollie.json with config values
47
+ const ollieConfigPath = path.join(targetDir, "ollie.json");
48
+
49
+ await fs.writeJSON(
50
+ ollieConfigPath,
51
+ {
52
+ storeId: storeId || null,
53
+ versionId: versionId || null,
54
+ platform,
55
+ platformStoreId: platformStoreId || null,
56
+ },
57
+ { spaces: 2 },
58
+ );
59
+
60
+ // Initialize git in the folder
61
+ try {
62
+ await exec("git init", { cwd: targetDir });
63
+ } catch (e) {
64
+ console.log("\n❌ Error initializing git:", e);
65
+ }
66
+
67
+ // Print success message and next steps
68
+ printSuccessMessage(
69
+ projectName,
70
+ storeId,
71
+ versionId,
72
+ platform,
73
+ platformStoreId,
74
+ );
75
+ }
76
+
77
+ /**
78
+ * Print success message with next steps
79
+ */
80
+ function printSuccessMessage(
81
+ projectName: string,
82
+ storeId?: string,
83
+ versionId?: string,
84
+ platform?: string,
85
+ platformStoreId?: string,
86
+ ): void {
87
+ console.log("\n✅ Project created successfully!");
88
+ console.log("\nNext steps:");
89
+ console.log(` cd ${chalk.cyan(projectName)}`);
90
+ console.log(
91
+ ` npm install (or ${chalk.cyan("pnpm install")} or ${chalk.cyan("yarn")})`,
92
+ );
93
+
94
+ console.log(
95
+ "\n The development environment leverages the Preview.js extension for VS Code:",
96
+ );
97
+ console.log(
98
+ ` ${chalk.cyan("https://marketplace.visualstudio.com/items?itemName=zenclabs.previewjs")}\n`,
99
+ );
100
+
101
+ // Display configuration information
102
+ if (storeId) {
103
+ console.log(`Store ID: ${chalk.green(storeId)}`);
104
+ } else if (platform && platformStoreId) {
105
+ console.log(`Platform: ${chalk.green(platform)}`);
106
+ console.log(`Platform Store ID: ${chalk.green(platformStoreId)}`);
107
+ }
108
+
109
+ if (versionId) {
110
+ console.log(`Version ID: ${chalk.green(versionId)}`);
111
+ }
112
+
113
+ console.log("\n🛍️ Happy development with Ollie Shop! 🛍️\n");
114
+ }
package/src/index.ts ADDED
@@ -0,0 +1,130 @@
1
+ import path from "node:path";
2
+ import chalk from "chalk";
3
+ import { Command } from "commander";
4
+ import fs from "fs-extra";
5
+
6
+ import { createProject } from "./commands/create-project";
7
+ import type { OllieShopConfig } from "./types";
8
+ import {
9
+ promptForOverwrite,
10
+ promptForPlatformStoreId,
11
+ promptForProjectName,
12
+ promptForStoreId,
13
+ promptForVersionId,
14
+ } from "./utils/prompts";
15
+
16
+ /**
17
+ * Main CLI entry point
18
+ */
19
+ async function main(): Promise<void> {
20
+ console.log(
21
+ chalk.bold.blue(
22
+ "🛍️ Create Ollie Shop - Bootstrap a new Ollie Shop project",
23
+ ),
24
+ );
25
+
26
+ // Set up Commander for argument parsing
27
+ const program = new Command()
28
+ .name("create-ollie-shop")
29
+ .description(
30
+ "Bootstrap a new Ollie Shop project with optional store configuration",
31
+ )
32
+ .version("0.1.0")
33
+ .argument("[project-name]", "Project folder name", "my-ollie-shop")
34
+ .option("--store-id <id>", "Specify an Ollie store ID")
35
+ .option("--version-id <id>", "Specify an Ollie store version ID")
36
+ .addHelpText(
37
+ "after",
38
+ `
39
+ Examples:
40
+ $ create-ollie-shop
41
+ $ create-ollie-shop my-awesome-store
42
+ $ create-ollie-shop my-store --store-id 7217542a-d7c6-40d3-a20e-db13b310a781 --version-id 882c8c18-550c-4ac5-9d3b-015d7dd25741
43
+ `,
44
+ )
45
+ .parse(process.argv);
46
+
47
+ try {
48
+ await initializeProject(program);
49
+ } catch (error) {
50
+ console.error(chalk.red("\n❌ Error:"), error);
51
+ process.exit(1);
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Process command line arguments and initialize the project
57
+ */
58
+ async function initializeProject(program: Command): Promise<void> {
59
+ // Get options from Commander
60
+ const options = program.opts();
61
+
62
+ // Extract CLI options
63
+ let storeId = options.storeId as string | undefined;
64
+ let versionId = options.versionId as string | undefined;
65
+
66
+ // For platform, we only support VTEX currently
67
+ const platform = "vtex";
68
+ let platformStoreId: string | undefined;
69
+
70
+ // Get or prompt for project name
71
+ let projectName = program.args[0];
72
+ if (!projectName) {
73
+ projectName = await promptForProjectName();
74
+ }
75
+
76
+ // Handle target directory
77
+ const cwd = process.cwd();
78
+ const targetDir = path.join(cwd, projectName);
79
+
80
+ // Check if target directory exists and confirm overwrite
81
+ if (fs.existsSync(targetDir)) {
82
+ const shouldOverwrite = await promptForOverwrite(projectName);
83
+
84
+ if (!shouldOverwrite) {
85
+ console.log(chalk.red("❌ Operation cancelled"));
86
+ return;
87
+ }
88
+
89
+ // Clean existing directory
90
+ await fs.emptyDir(targetDir);
91
+ }
92
+
93
+ // Handle store identification
94
+ if (!storeId) {
95
+ // First ask for a direct store ID
96
+ storeId = await promptForStoreId();
97
+
98
+ // If no store ID is provided, get platform store ID
99
+ if (!storeId) {
100
+ console.log(
101
+ chalk.blue(
102
+ `Using ${chalk.bold(platform.toUpperCase())} as the platform`,
103
+ ),
104
+ );
105
+
106
+ platformStoreId = await promptForPlatformStoreId(platform);
107
+ }
108
+ }
109
+
110
+ // Get version ID if not provided
111
+ if (!versionId) {
112
+ versionId = await promptForVersionId();
113
+ }
114
+
115
+ // Create configuration object
116
+ const config: OllieShopConfig = {
117
+ projectName,
118
+ storeId,
119
+ versionId,
120
+ platform,
121
+ platformStoreId,
122
+ targetDir,
123
+ };
124
+
125
+ // Create the project
126
+ await createProject(config);
127
+ }
128
+
129
+ // Start the CLI
130
+ main();
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Configuration options for creating a new Ollie Shop project
3
+ */
4
+ export type OllieShopConfig = {
5
+ /** The name of the project */
6
+ projectName: string;
7
+ /** Optional Ollie store ID */
8
+ storeId?: string;
9
+ /** Optional version ID */
10
+ versionId?: string;
11
+ /** E-commerce platform (e.g., "vtex") */
12
+ platform?: string;
13
+ /** Store/Account ID from the platform */
14
+ platformStoreId?: string;
15
+ /** Target directory path */
16
+ targetDir: string;
17
+ };
@@ -0,0 +1,85 @@
1
+ import chalk from "chalk";
2
+ import prompts from "prompts";
3
+ import { z } from "zod";
4
+
5
+ /**
6
+ * Prompt for project name
7
+ */
8
+ export async function promptForProjectName(
9
+ defaultValue = "my-ollie-shop",
10
+ ): Promise<string> {
11
+ const { value } = await prompts({
12
+ type: "text",
13
+ name: "value",
14
+ message: "Project name:",
15
+ initial: defaultValue,
16
+ validate: (value) => z.string().nonempty().safeParse(value).success,
17
+ });
18
+
19
+ return value || defaultValue;
20
+ }
21
+
22
+ /**
23
+ * Prompt for directory overwrite confirmation
24
+ */
25
+ export async function promptForOverwrite(dirName: string): Promise<boolean> {
26
+ const { overwrite } = await prompts({
27
+ type: "confirm",
28
+ name: "overwrite",
29
+ message: `Directory ${chalk.cyan(dirName)} already exists. Overwrite?`,
30
+ initial: false,
31
+ });
32
+
33
+ return overwrite;
34
+ }
35
+
36
+ /**
37
+ * Prompt for store ID
38
+ */
39
+ export async function promptForStoreId(): Promise<string | undefined> {
40
+ const { value } = await prompts({
41
+ type: "text",
42
+ name: "value",
43
+ message: "Store ID (optional):",
44
+ hint: "Leave empty if you want to set up the workspace manually",
45
+ validate: (value) =>
46
+ z.string().uuid().or(z.literal("")).safeParse(value).success ||
47
+ "Please enter a valid UUID or leave empty",
48
+ });
49
+
50
+ // Return undefined if empty string
51
+ return value && value.trim() !== "" ? value : undefined;
52
+ }
53
+
54
+ /**
55
+ * Prompt for platform store ID
56
+ */
57
+ export async function promptForPlatformStoreId(
58
+ platform: string,
59
+ ): Promise<string> {
60
+ const { platformStoreId } = await prompts({
61
+ type: "text",
62
+ name: "platformStoreId",
63
+ message: `Enter your ${platform.toUpperCase()} account name:`,
64
+ validate: (value) =>
65
+ z.string().min(1).safeParse(value).success ||
66
+ "Account name cannot be empty",
67
+ });
68
+
69
+ return platformStoreId;
70
+ }
71
+
72
+ /**
73
+ * Prompt for version ID
74
+ */
75
+ export async function promptForVersionId(): Promise<string | undefined> {
76
+ const { value } = await prompts({
77
+ type: "text",
78
+ name: "value",
79
+ message: "Version ID (optional):",
80
+ validate: (value) =>
81
+ z.string().uuid().or(z.literal("")).safeParse(value).success,
82
+ });
83
+
84
+ return value && value.trim() !== "" ? value : undefined;
85
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "recommendations": ["biomejs.biome", "zenclabs.previewjs"]
3
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "editor.formatOnSave": true,
3
+ "editor.defaultFormatter": "biomejs.biome",
4
+ "editor.codeActionsOnSave": {
5
+ "source.fixAll.biome": "explicit",
6
+ "source.organizeImports.biome": "explicit"
7
+ },
8
+ "[typescript]": {
9
+ "editor.defaultFormatter": "biomejs.biome"
10
+ },
11
+ "[typescriptreact]": {
12
+ "editor.defaultFormatter": "biomejs.biome"
13
+ },
14
+ "[javascript]": {
15
+ "editor.defaultFormatter": "biomejs.biome"
16
+ },
17
+ "[javascriptreact]": {
18
+ "editor.defaultFormatter": "biomejs.biome"
19
+ },
20
+ "[json]": {
21
+ "editor.defaultFormatter": "biomejs.biome"
22
+ },
23
+ "[jsonc]": {
24
+ "editor.defaultFormatter": "biomejs.biome"
25
+ }
26
+ }
@@ -0,0 +1,10 @@
1
+ import styles from "./styles.module.css";
2
+
3
+ export default function HelloWorld({ name = "Stranger" }: { name?: string }) {
4
+ return (
5
+ <div className={styles.container}>
6
+ <h1>Hello, {name}!</h1>
7
+ <p>Test</p>
8
+ </div>
9
+ );
10
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": null,
3
+ "name": "Hello",
4
+ "props": null,
5
+ "src": "components/hello"
6
+ }
@@ -0,0 +1,7 @@
1
+ .container {
2
+ display: flex;
3
+ justify-content: center;
4
+ align-items: center;
5
+ flex-direction: column;
6
+ gap: 1rem;
7
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "storeId": null,
3
+ "versionId": null,
4
+ "platform": "vtex",
5
+ "platformStoreId": null,
6
+ "sessionId": "8be5691a4398411c875762eefc94b57c",
7
+ "props": null,
8
+ "theme": {}
9
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "dev",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "devDependencies": {
6
+ "@ollie-shop/sdk": "^0.1.0",
7
+ "@ollie-shop/previewjs": "^0.1.0",
8
+ "@types/node": "^22.14.0",
9
+ "@types/react": "^19.1.0",
10
+ "@types/react-dom": "^19.1.1",
11
+ "next": "^15.3.1",
12
+ "react": "^19.0.0",
13
+ "react-dom": "^19.0.0",
14
+ "typescript": "^5.7.3",
15
+ "vite": "^6.2.5"
16
+ }
17
+ }
@@ -0,0 +1,3 @@
1
+ import { defineConfig } from "@ollie-shop/previewjs";
2
+
3
+ export default defineConfig();
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["ES2022"],
4
+ "module": "ES2022",
5
+ "target": "ES2022",
6
+ "moduleResolution": "Bundler",
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "declaration": true,
10
+ "noUncheckedIndexedAccess": true,
11
+ "skipLibCheck": true,
12
+ "esModuleInterop": true,
13
+ "resolveJsonModule": true,
14
+ "typeRoots": ["./node_modules/@types", "./types"],
15
+ "types": ["node"]
16
+ },
17
+ "exclude": ["node_modules", "dist"]
18
+ }
@@ -0,0 +1,4 @@
1
+ declare module "*.css" {
2
+ const classes: { readonly [key: string]: string };
3
+ export default classes;
4
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["ES2022"],
4
+ "module": "ES2022",
5
+ "target": "ES2022",
6
+ "moduleResolution": "Bundler",
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "declaration": true,
10
+ "noUncheckedIndexedAccess": true,
11
+ "skipLibCheck": true,
12
+ "esModuleInterop": true,
13
+ "composite": true,
14
+ "resolveJsonModule": true,
15
+ "types": ["node", "vitest/globals"],
16
+ "paths": {
17
+ "@/*": ["./src/*"]
18
+ }
19
+ },
20
+ "exclude": ["node_modules", "dist"]
21
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,43 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { defineConfig } from "tsup";
4
+
5
+ const EXCLUDED = [
6
+ // Node files
7
+ "node_modules",
8
+ "dist",
9
+ // Config files
10
+ "ollie.json",
11
+ "meta.json",
12
+ // Lock files
13
+ "pnpm-lock.yaml",
14
+ "package-lock.json",
15
+ "yarn.lock",
16
+ ];
17
+
18
+ const REGEX_EXCLUDED = new RegExp(EXCLUDED.join("|"));
19
+
20
+ export default defineConfig({
21
+ entry: ["src/index.ts"],
22
+ format: "esm",
23
+ sourcemap: false,
24
+ bundle: true,
25
+ clean: true,
26
+ banner: {
27
+ js: "#!/usr/bin/env node",
28
+ },
29
+ plugins: [
30
+ {
31
+ name: "copy-template",
32
+ buildEnd: async () => {
33
+ const templateDir = path.resolve("template");
34
+ const destDir = path.resolve("dist/template");
35
+
36
+ await fs.cp(templateDir, destDir, {
37
+ recursive: true,
38
+ filter: (src: string) => !REGEX_EXCLUDED.test(path.basename(src)),
39
+ });
40
+ },
41
+ },
42
+ ],
43
+ });
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ },
7
+ });