juo 0.0.1-alpha.2 → 0.0.1-alpha.4

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.
Files changed (35) hide show
  1. package/README.md +42 -2
  2. package/bin/dev.js +1 -1
  3. package/dist/analytics-B2gwRg5Q.js +69 -0
  4. package/dist/commands/create.d.ts +15 -0
  5. package/dist/commands/create.js +465 -0
  6. package/dist/commands/generate.d.ts +20 -0
  7. package/dist/commands/generate.js +3 -0
  8. package/dist/commands/publish/index.d.ts +41 -13
  9. package/dist/commands/publish/index.js +193 -114
  10. package/dist/generate-D7J6nPBd.js +252 -0
  11. package/dist/hooks/postrun.d.ts +6 -0
  12. package/dist/hooks/postrun.js +62 -0
  13. package/dist/hooks/prerun.d.ts +6 -0
  14. package/dist/hooks/prerun.js +15 -0
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.js +3 -1
  17. package/oclif.manifest.json +89 -1
  18. package/package.json +13 -4
  19. package/templates/.storybook/preview-head.html.liquid +11 -0
  20. package/templates/block/preact/index.ts.liquid +40 -0
  21. package/templates/block/preact/{{block.clsName}}.tsx.liquid +200 -0
  22. package/templates/block/react/index.ts.liquid +41 -0
  23. package/templates/block/react/state.ts.liquid +105 -0
  24. package/templates/block/react/{{block.clsName}}.tsx.liquid +91 -0
  25. package/templates/configFiles/.env.liquid +0 -0
  26. package/templates/configFiles/.gitignore.liquid +6 -0
  27. package/templates/configFiles/.prettierignore.liquid +45 -0
  28. package/templates/configFiles/.prettierrc.liquid +1 -0
  29. package/templates/configFiles/vite-env.d.ts.liquid +2 -0
  30. package/templates/css/styles.css +71 -0
  31. package/templates/stories/{{block.clsName}}.stories.tsx.liquid +24 -0
  32. package/templates/tailwind/src/tailwind.css.liquid +1 -0
  33. package/templates/tailwind/vite.config.ts.liquid +23 -0
  34. package/dist/base-command.d.ts +0 -23
  35. package/dist/base-command.js +0 -122
@@ -1,119 +1,198 @@
1
- import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
2
- import { Flags } from '@oclif/core';
3
- import mime from 'mime';
4
- import fs from 'node:fs';
5
- import path from 'node:path';
6
- import asyncPool from 'tiny-async-pool';
7
- import { BaseCommand } from '../../base-command.js';
8
- export default class Publish extends BaseCommand {
9
- static args = {};
10
- static description = ' Publish theme files from the ./dist directory to the remote registry.';
11
- static examples = [
12
- `<%= config.bin %> publish
1
+ import { Command, Flags } from "@oclif/core";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
5
+ import mime from "mime";
6
+ import asyncPool from "tiny-async-pool";
7
+ import inquirer from "inquirer";
8
+
9
+ //#region src/base-command.ts
10
+ var BaseCommand = class extends Command {
11
+ static baseFlags = { reconfigure: Flags.boolean({
12
+ default: false,
13
+ description: "Reconfigure settings",
14
+ helpGroup: "GLOBAL"
15
+ }) };
16
+ args;
17
+ flags;
18
+ get configFileName() {
19
+ return "config.json";
20
+ }
21
+ get configFilePath() {
22
+ return path.join(this.config.configDir, this.configFileName);
23
+ }
24
+ configExists() {
25
+ return fs.existsSync(this.configFilePath);
26
+ }
27
+ async ensureConfig(reconfigure = false) {
28
+ let config = {};
29
+ if (this.configExists()) try {
30
+ config = this.loadConfig();
31
+ if (this.validateConfig(config) && !reconfigure) {
32
+ this.log(`Found configuration at ${this.configFilePath}`);
33
+ return config;
34
+ }
35
+ } catch {
36
+ this.log(`Warning: Could not parse existing config at ${this.configFilePath}`);
37
+ }
38
+ if (reconfigure && this.validateConfig(config)) this.log("Reconfiguring settings...");
39
+ else this.log("Configuration incomplete. Setting up...");
40
+ const questions = [];
41
+ if (!config.JUO_API_URL || reconfigure) questions.push({
42
+ default: "https://api.juo.io",
43
+ message: "Enter the Juo API URL:",
44
+ name: "JUO_API_URL",
45
+ type: "input",
46
+ validate(input) {
47
+ if (!input.trim()) return "Juo API URL is required";
48
+ return true;
49
+ }
50
+ });
51
+ if (!config.JUO_API_KEY || reconfigure) questions.push({
52
+ message: "Enter your Juo API key:",
53
+ name: "JUO_API_KEY",
54
+ type: "password",
55
+ validate(input) {
56
+ if (!input.trim()) return "Juo API key is required";
57
+ return true;
58
+ }
59
+ });
60
+ if (questions.length === 0) return config;
61
+ const response = await inquirer.prompt(questions);
62
+ if (response.JUO_API_URL) config.JUO_API_URL = response.JUO_API_URL.trim();
63
+ if (response.JUO_API_KEY) config.JUO_API_KEY = response.JUO_API_KEY.trim();
64
+ this.saveConfig(config);
65
+ this.log(`Saved configuration to ${this.configFilePath}`);
66
+ if (this.validateConfig(config)) return config;
67
+ throw new Error("Failed to create valid configuration");
68
+ }
69
+ async init() {
70
+ await super.init();
71
+ const { args, flags } = await this.parse({
72
+ args: this.ctor.args,
73
+ baseFlags: super.ctor.baseFlags,
74
+ flags: this.ctor.flags,
75
+ strict: this.ctor.strict
76
+ });
77
+ this.flags = flags;
78
+ this.args = args;
79
+ }
80
+ loadConfig() {
81
+ if (!this.configExists()) throw new Error(`Configuration file not found at ${this.configFilePath}`);
82
+ try {
83
+ const configContent = fs.readFileSync(this.configFilePath, "utf8");
84
+ return JSON.parse(configContent);
85
+ } catch (error) {
86
+ throw new Error(`Failed to parse configuration file at ${this.configFilePath}: ${error}`);
87
+ }
88
+ }
89
+ saveConfig(config) {
90
+ fs.mkdirSync(this.config.configDir, { recursive: true });
91
+ fs.writeFileSync(this.configFilePath, JSON.stringify(config, null, 2), { encoding: "utf8" });
92
+ }
93
+ validateConfig(config) {
94
+ return Boolean(config.JUO_API_KEY && config.JUO_API_KEY.length > 0 && config.JUO_API_URL && config.JUO_API_URL.length > 0);
95
+ }
96
+ };
97
+
98
+ //#endregion
99
+ //#region src/commands/publish/index.ts
100
+ var Publish = class extends BaseCommand {
101
+ static args = {};
102
+ static description = " Publish theme files from the ./dist directory to the remote registry.";
103
+ static examples = [
104
+ `<%= config.bin %> publish
13
105
  Publish the default theme files.
14
106
  `,
15
- `<%= config.bin %> publish --theme custom-theme
107
+ `<%= config.bin %> publish --theme custom-theme
16
108
  Publish a custom theme files.
17
109
  `,
18
- `<%= config.bin %> publish --reconfigure
110
+ `<%= config.bin %> publish --reconfigure
19
111
  Reconfigure settings and publish.
20
- `,
21
- ];
22
- static flags = {
23
- theme: Flags.string({
24
- char: 't',
25
- default: 'default',
26
- description: 'Specify the name of the theme to publish',
27
- }),
28
- };
29
- getAllFiles(dirPath, basePath) {
30
- const files = [];
31
- const items = fs.readdirSync(dirPath);
32
- for (const item of items) {
33
- const fullPath = path.join(dirPath, item);
34
- const stat = fs.statSync(fullPath);
35
- if (stat.isFile()) {
36
- const relativePath = path.relative(basePath, fullPath);
37
- files.push(relativePath);
38
- }
39
- }
40
- return files;
41
- }
42
- async run() {
43
- this.log(`🚀 Publishing ${this.flags.theme} theme files to registry...`);
44
- try {
45
- const config = await this.ensureConfig(this.flags.reconfigure);
46
- this.log('📤 Uploading files...');
47
- await this.uploadFiles(this.flags.theme, config);
48
- this.log('✅ Publishing completed successfully!');
49
- }
50
- catch (error) {
51
- this.error(`❌ Publishing failed: ${error instanceof Error ? error.message : String(error)}`);
52
- }
53
- }
54
- async getAwsConnectionInfo(config, themeName) {
55
- try {
56
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
57
- const response = await fetch(`${config.JUO_API_URL}/get-publish-token`, {
58
- body: JSON.stringify({ theme: themeName }),
59
- headers: {
60
- Accept: 'application/json',
61
- 'Content-Type': 'application/json',
62
- 'X-Juo-Admin-Api-Key': config.JUO_API_KEY,
63
- },
64
- method: 'POST',
65
- });
66
- if (response.status !== 200) {
67
- this.error(`API request failed with status ${response.status}`);
68
- }
69
- if (response.headers.get('Content-Type')?.includes('application/json')) {
70
- return (await response.json());
71
- }
72
- return null;
73
- }
74
- catch (error) {
75
- this.error(`Failed to fetch credentials: ${error instanceof Error ? error.message : String(error)}`);
76
- }
77
- }
78
- async uploadFile(client, distDir, file, options) {
79
- if (file.split('/').some((item) => item.startsWith('.'))) {
80
- return file;
81
- }
82
- const filepath = path.join(distDir, file);
83
- const stream = fs.createReadStream(filepath);
84
- const command = new PutObjectCommand({
85
- Body: stream,
86
- Bucket: options.bucket,
87
- CacheControl: 'max-age=3600, must-revalidate',
88
- ContentType: mime.getType(file) || 'application/octet-stream',
89
- Key: `${options.prefix}${file}`,
90
- });
91
- this.log(`📤 Uploading file '${file}'...`);
92
- await client.send(command);
93
- return file;
94
- }
95
- async uploadFiles(themeName, config) {
96
- const connectionInfo = await this.getAwsConnectionInfo(config, themeName);
97
- if (!connectionInfo) {
98
- throw new Error('Failed to get AWS connection info');
99
- }
100
- const { bucket, credentials, prefix, region } = connectionInfo;
101
- const client = new S3Client({
102
- credentials,
103
- region,
104
- });
105
- const distDir = path.resolve(process.cwd(), 'dist');
106
- if (!fs.existsSync(distDir)) {
107
- this.error('dist directory not found');
108
- }
109
- const files = this.getAllFiles(distDir, distDir);
110
- if (files.length === 0) {
111
- this.log('No files found in dist directory');
112
- return;
113
- }
114
- this.log(`Found ${files.length} file(s) to upload`);
115
- for await (const file of asyncPool(4, files, (file) => this.uploadFile(client, distDir, file, { bucket, prefix }))) {
116
- this.log(`✅ Uploaded file '${file}'`);
117
- }
118
- }
119
- }
112
+ `
113
+ ];
114
+ static flags = { theme: Flags.string({
115
+ char: "t",
116
+ default: "default",
117
+ description: "Specify the name of the theme to publish"
118
+ }) };
119
+ getAllFiles(dirPath, basePath) {
120
+ const files = [];
121
+ const items = fs.readdirSync(dirPath);
122
+ for (const item of items) {
123
+ const fullPath = path.join(dirPath, item);
124
+ if (fs.statSync(fullPath).isFile()) {
125
+ const relativePath = path.relative(basePath, fullPath);
126
+ files.push(relativePath);
127
+ }
128
+ }
129
+ return files;
130
+ }
131
+ async run() {
132
+ this.log(`🚀 Publishing ${this.flags.theme} theme files to registry...`);
133
+ try {
134
+ const config = await this.ensureConfig(this.flags.reconfigure);
135
+ this.log("📤 Uploading files...");
136
+ await this.uploadFiles(this.flags.theme, config);
137
+ this.log("✅ Publishing completed successfully!");
138
+ } catch (error) {
139
+ this.error(`❌ Publishing failed: ${error instanceof Error ? error.message : String(error)}`);
140
+ }
141
+ }
142
+ async getAwsConnectionInfo(config, themeName) {
143
+ try {
144
+ const response = await fetch(`${config.JUO_API_URL}/merchant/get-publish-token`, {
145
+ body: JSON.stringify({ theme: themeName }),
146
+ headers: {
147
+ Accept: "application/json",
148
+ "Content-Type": "application/json",
149
+ "X-Juo-Access-Token": config.JUO_API_KEY
150
+ },
151
+ method: "POST"
152
+ });
153
+ if (response.status !== 200) this.error(`API request failed with status ${response.status}`);
154
+ if (response.headers.get("Content-Type")?.includes("application/json")) return await response.json();
155
+ return null;
156
+ } catch (error) {
157
+ this.error(`Failed to fetch credentials: ${error instanceof Error ? error.message : String(error)}`);
158
+ }
159
+ }
160
+ async uploadFile(client, distDir, file, options) {
161
+ if (file.split("/").some((item) => item.startsWith("."))) return file;
162
+ const filepath = path.join(distDir, file);
163
+ const command = new PutObjectCommand({
164
+ Body: fs.createReadStream(filepath),
165
+ Bucket: options.bucket,
166
+ CacheControl: "max-age=3600, must-revalidate",
167
+ ContentType: mime.getType(file) || "application/octet-stream",
168
+ Key: `${options.prefix}${file}`
169
+ });
170
+ this.log(`📤 Uploading file '${file}'...`);
171
+ await client.send(command);
172
+ return file;
173
+ }
174
+ async uploadFiles(themeName, config) {
175
+ const connectionInfo = await this.getAwsConnectionInfo(config, themeName);
176
+ if (!connectionInfo) throw new Error("Failed to get AWS connection info");
177
+ const { bucket, credentials, prefix, region } = connectionInfo;
178
+ const client = new S3Client({
179
+ credentials,
180
+ region
181
+ });
182
+ const distDir = path.resolve(process.cwd(), "dist");
183
+ if (!fs.existsSync(distDir)) this.error("dist directory not found");
184
+ const files = this.getAllFiles(distDir, distDir);
185
+ if (files.length === 0) {
186
+ this.log("No files found in dist directory");
187
+ return;
188
+ }
189
+ this.log(`Found ${files.length} file(s) to upload`);
190
+ for await (const file of asyncPool(4, files, (file$1) => this.uploadFile(client, distDir, file$1, {
191
+ bucket,
192
+ prefix
193
+ }))) this.log(`✅ Uploaded file '${file}'`);
194
+ }
195
+ };
196
+
197
+ //#endregion
198
+ export { Publish as default };
@@ -0,0 +1,252 @@
1
+ import { Command, Flags } from "@oclif/core";
2
+ import { isCancel, log, select, spinner, text } from "@clack/prompts";
3
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
4
+ import path from "node:path";
5
+ import { detectPackageManager } from "nypm";
6
+ import slugify from "slugify";
7
+ import { Liquid } from "liquidjs";
8
+ import { fileURLToPath } from "node:url";
9
+ import { readPackageJSON } from "pkg-types";
10
+
11
+ //#region src/utils/baseTemplateGenerator.ts
12
+ var BaseTemplateGenerator = class {
13
+ renderTemplate(templateName, destDir, scope, fileFilter) {
14
+ try {
15
+ const templateDir = this.getTemplateDir(templateName);
16
+ const engine = new Liquid();
17
+ const files = this.getTemplateFiles(templateName);
18
+ const filteredFiles = fileFilter ? files.filter(fileFilter) : files;
19
+ for (const file of filteredFiles) {
20
+ const filename = engine.parseAndRenderSync(file, scope).replace(/\.liquid$/, "");
21
+ const tplPath = path.join(templateDir, file);
22
+ const destPath = path.join(destDir, filename);
23
+ mkdirSync(path.dirname(destPath), { recursive: true });
24
+ writeFileSync(destPath, engine.parseAndRenderSync(readFileSync(tplPath, "utf-8"), scope));
25
+ }
26
+ } catch (error) {
27
+ throw new Error(`Error rendering template ${templateName}: ${error}`);
28
+ }
29
+ }
30
+ getTemplateDir(name) {
31
+ const packageRoot = this.resolvePackageRoot();
32
+ return path.join(packageRoot, "templates", name);
33
+ }
34
+ getTemplateFiles(name) {
35
+ function scan(dirpath, baseDir) {
36
+ return readdirSync(dirpath).flatMap((filename) => {
37
+ const filepath = path.join(dirpath, filename);
38
+ if (statSync(filepath).isDirectory()) return scan(filepath, baseDir);
39
+ else return [path.relative(baseDir, filepath)];
40
+ });
41
+ }
42
+ const templateDir = this.getTemplateDir(name);
43
+ return scan(templateDir, templateDir);
44
+ }
45
+ resolvePackageRoot() {
46
+ let currentPath = path.dirname(fileURLToPath(import.meta.url));
47
+ while (!existsSync(path.join(currentPath, "package.json"))) {
48
+ const parentDir = path.dirname(currentPath);
49
+ if (parentDir === currentPath) throw new Error("Could not find package root");
50
+ currentPath = parentDir;
51
+ }
52
+ return currentPath;
53
+ }
54
+ };
55
+
56
+ //#endregion
57
+ //#region src/utils/storyGenerator.ts
58
+ var StoryGenerator = class extends BaseTemplateGenerator {
59
+ async generate(destDir, options) {
60
+ if (options.type === "block") await this.generateBlockTemplate(destDir, options);
61
+ else await this.generateThemeTemplate(destDir, options);
62
+ }
63
+ async generateBlockTemplate(destDir, options) {
64
+ const scope = { block: {
65
+ slug: slugify(options.name, { lower: true }),
66
+ name: options.name,
67
+ clsName: slugify(options.name, { lower: true }).split("-").map((item) => `${item[0].toUpperCase()}${item.substring(1)}`).join(""),
68
+ framework: options.framework
69
+ } };
70
+ if (["react", "preact"].includes(scope.block.framework)) {
71
+ if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
72
+ this.renderTemplate("stories", destDir, scope);
73
+ } else options.logger?.("Story generation skipped");
74
+ }
75
+ async generateThemeTemplate(destDir, options) {
76
+ const themeScope = { theme: {
77
+ name: options.name,
78
+ slug: slugify(options.name, { lower: true }),
79
+ framework: options.framework,
80
+ clsName: slugify(options.name, { lower: true }).split("-").map((item) => `${item[0].toUpperCase()}${item.substring(1)}`).join("")
81
+ } };
82
+ if (options.framework === "react") this.renderTemplate("theme/react", destDir, themeScope, (filename) => filename.includes(".stories.tsx.liquid"));
83
+ }
84
+ };
85
+
86
+ //#endregion
87
+ //#region src/utils/juoTemplateGenerator.ts
88
+ var JuoTemplateGenerator = class extends BaseTemplateGenerator {
89
+ storyGenerator;
90
+ constructor() {
91
+ super();
92
+ this.storyGenerator = new StoryGenerator();
93
+ }
94
+ async generate(destDir, options) {
95
+ await this.generateBlockTemplate(destDir, options);
96
+ }
97
+ async generateBlockTemplate(destDir, options) {
98
+ const scope = { block: {
99
+ slug: slugify(options.name, { lower: true }),
100
+ name: options.name,
101
+ clsName: slugify(options.name, { lower: true }).split("-").map((item) => `${item[0].toUpperCase()}${item.substring(1)}`).join(""),
102
+ tailwind: options.tailwind,
103
+ framework: options.framework
104
+ } };
105
+ this.renderTemplate(`${options.type}/${options.framework}`, path.join(destDir, scope.block.slug), scope);
106
+ options.logger?.("Adding block to theme");
107
+ try {
108
+ appendFileSync(path.join(destDir, "index.ts"), `export { ${scope.block.clsName}Block } from "./${scope.block.slug}";\n`);
109
+ } catch (error) {
110
+ options.logger?.("Error adding block to theme: " + error);
111
+ throw error;
112
+ }
113
+ if (options.story) {
114
+ const projectRoot = path.dirname(path.dirname(destDir));
115
+ const storiesDir = path.join(projectRoot, "stories");
116
+ try {
117
+ await this.storyGenerator.generate(storiesDir, {
118
+ name: scope.block.name,
119
+ type: "block",
120
+ framework: options.framework,
121
+ tailwind: options.tailwind
122
+ });
123
+ } catch (error) {
124
+ options.logger?.("Error generating story: " + error);
125
+ throw error;
126
+ }
127
+ } else options.logger?.(`Story generation skipped (options.story = ${options.story})`);
128
+ }
129
+ appendStylesheet(destDir) {
130
+ this.renderTemplate("css", path.join(destDir, "src"), {});
131
+ }
132
+ };
133
+
134
+ //#endregion
135
+ //#region src/utils/resolveFramework.ts
136
+ async function resolveFramework() {
137
+ try {
138
+ const packageJson = await readPackageJSON();
139
+ if (!packageJson) throw new Error("Could not find package.json");
140
+ if (packageJson.dependencies == null) throw new Error("Could not find dependencies in package.json");
141
+ if ("preact" in packageJson.dependencies) return "preact";
142
+ return "react";
143
+ } catch (error) {
144
+ throw new Error(`Error resolving framework: ${error}`);
145
+ }
146
+ }
147
+
148
+ //#endregion
149
+ //#region src/commands/generate.ts
150
+ var Generate = class Generate extends Command {
151
+ static id = "generate";
152
+ static description = "Generate blocks for your project";
153
+ static flags = {
154
+ name: Flags.string({
155
+ char: "n",
156
+ description: "The name of the component"
157
+ }),
158
+ type: Flags.string({
159
+ description: "The type of the component",
160
+ options: ["block"],
161
+ default: "block"
162
+ }),
163
+ tailwind: Flags.boolean({
164
+ char: "t",
165
+ default: void 0,
166
+ allowNo: true
167
+ }),
168
+ verbose: Flags.boolean({
169
+ char: "v",
170
+ default: true,
171
+ allowNo: true
172
+ })
173
+ };
174
+ static examples = ["<%= config.bin %> <%= command.id %>"];
175
+ resolvePackageJsonPath() {
176
+ let currentPath = path.resolve("./");
177
+ while (!existsSync(path.join(currentPath, "package.json"))) {
178
+ const parentDir = path.dirname(currentPath);
179
+ if (parentDir === currentPath) throw new Error("Could not find 'package.json' in the project");
180
+ currentPath = parentDir;
181
+ }
182
+ return currentPath;
183
+ }
184
+ async resolveCSSFramework(dir) {
185
+ try {
186
+ const packageJson = await readPackageJSON(path.join(dir, "package.json"));
187
+ if (packageJson?.dependencies?.tailwindcss || packageJson?.devDependencies?.tailwindcss) return "tailwind";
188
+ return false;
189
+ } catch (error) {
190
+ throw new Error(`Error resolving CSS framework: ${error}`);
191
+ }
192
+ }
193
+ async run() {
194
+ const { flags } = await this.parse(Generate);
195
+ let rootDir;
196
+ const templateGenerator = new JuoTemplateGenerator();
197
+ try {
198
+ rootDir = this.resolvePackageJsonPath();
199
+ } catch (error) {
200
+ throw new Error(`Error resolving package JSON path: ${error}`);
201
+ }
202
+ const tailwind = flags.tailwind ?? await this.resolveCSSFramework(rootDir);
203
+ if (isCancel(flags.type ?? await select({
204
+ message: "What you want to create?",
205
+ options: [
206
+ {
207
+ value: "block",
208
+ label: "Block"
209
+ },
210
+ {
211
+ value: "page",
212
+ label: "Page"
213
+ },
214
+ {
215
+ value: "theme",
216
+ label: "Theme"
217
+ }
218
+ ],
219
+ initialValue: "block"
220
+ }))) process.exit(0);
221
+ const generateSpinner = flags.verbose ? spinner() : null;
222
+ const blocksDir = path.join(rootDir, "src/blocks");
223
+ const name = flags.name ?? await text({
224
+ message: "What is the name of the component?",
225
+ initialValue: "Starter Block",
226
+ validate(value) {
227
+ if (value.length === 0) return `Value is required!`;
228
+ }
229
+ });
230
+ if (isCancel(name)) this.exit(0);
231
+ const framework = await resolveFramework();
232
+ generateSpinner?.start();
233
+ generateSpinner?.message("Detecting package manager");
234
+ const packageManager = await detectPackageManager(rootDir);
235
+ if (!packageManager) throw new Error("Could not detect package manager");
236
+ if (flags.verbose) log.info("Package manager detected: " + packageManager.name);
237
+ generateSpinner?.message("Generating block");
238
+ await templateGenerator.generate(blocksDir, {
239
+ type: "block",
240
+ framework,
241
+ name,
242
+ tailwind,
243
+ logger: (msg) => generateSpinner?.message(msg),
244
+ story: true
245
+ });
246
+ templateGenerator.appendStylesheet(rootDir);
247
+ generateSpinner?.stop("Generated block");
248
+ }
249
+ };
250
+
251
+ //#endregion
252
+ export { BaseTemplateGenerator as n, Generate as t };
@@ -0,0 +1,6 @@
1
+ import { Hook } from "@oclif/core";
2
+
3
+ //#region src/hooks/postrun.d.ts
4
+ declare const hook: Hook.Postrun;
5
+ //#endregion
6
+ export { hook as default };
@@ -0,0 +1,62 @@
1
+ import { a as CUSTOMERIO_BETA_FORM_URL, i as CUSTOMERIO_API_KEY, n as trackCommandCompleted, o as CUSTOMERIO_SITE_ID, t as trackBetaSignup } from "../analytics-B2gwRg5Q.js";
2
+ import { confirm, isCancel, text } from "@clack/prompts";
3
+ import debug from "debug";
4
+
5
+ //#region src/utils/betaSignup.ts
6
+ const log$1 = debug("juo:betaSignup");
7
+ async function sendToCustomerIO(email) {
8
+ if (!CUSTOMERIO_BETA_FORM_URL || !CUSTOMERIO_SITE_ID || !CUSTOMERIO_API_KEY) return;
9
+ try {
10
+ const basicAuth = Buffer.from(`${CUSTOMERIO_SITE_ID}:${CUSTOMERIO_API_KEY}`).toString("base64");
11
+ await fetch(CUSTOMERIO_BETA_FORM_URL, {
12
+ method: "POST",
13
+ headers: {
14
+ "Content-Type": "application/json",
15
+ Authorization: `Basic ${basicAuth}`
16
+ },
17
+ body: JSON.stringify({ data: {
18
+ email,
19
+ source: "cli"
20
+ } })
21
+ }).catch((error) => {
22
+ log$1("Failed to send beta signup to CustomerIO: %O", error);
23
+ });
24
+ } catch (error) {
25
+ log$1("Error sending beta signup to CustomerIO: %O", error);
26
+ }
27
+ }
28
+ async function promptBetaSignup(commandContext) {
29
+ try {
30
+ const shouldSignup = await confirm({
31
+ message: "Would you like to request access to the private beta?",
32
+ initialValue: true
33
+ });
34
+ if (isCancel(shouldSignup) || !shouldSignup) return;
35
+ const email = await text({
36
+ message: "Enter your email address:",
37
+ placeholder: "your.email@example.com",
38
+ validate(value) {
39
+ if (value.length === 0) return "Email is required!";
40
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return "Please enter a valid email address";
41
+ }
42
+ });
43
+ if (isCancel(email)) return;
44
+ sendToCustomerIO(email).catch((error) => {
45
+ log$1("Failed to send beta signup request: %O", error);
46
+ });
47
+ trackBetaSignup(email, commandContext);
48
+ } catch (error) {
49
+ log$1("Error in beta signup prompt: %O", error);
50
+ }
51
+ }
52
+
53
+ //#endregion
54
+ //#region src/hooks/postrun.ts
55
+ const hook = async function(options) {
56
+ if (options.Command) trackCommandCompleted(options.Command.id || options.Command.name || "unknown", true);
57
+ if (options.Command) await promptBetaSignup(options.Command.id || options.Command.name || "unknown");
58
+ };
59
+ var postrun_default = hook;
60
+
61
+ //#endregion
62
+ export { postrun_default as default };
@@ -0,0 +1,6 @@
1
+ import { Hook } from "@oclif/core";
2
+
3
+ //#region src/hooks/prerun.d.ts
4
+ declare const hook: Hook.Prerun;
5
+ //#endregion
6
+ export { hook as default };
@@ -0,0 +1,15 @@
1
+ import { r as trackCommandStarted } from "../analytics-B2gwRg5Q.js";
2
+
3
+ //#region src/hooks/prerun.ts
4
+ const hook = async function(options) {
5
+ if (options.Command) {
6
+ const commandName = options.Command.id || options.Command.name || "unknown";
7
+ const properties = {};
8
+ if (options.argv && options.argv.length > 0) properties.argv = options.argv;
9
+ trackCommandStarted(commandName, properties);
10
+ }
11
+ };
12
+ var prerun_default = hook;
13
+
14
+ //#endregion
15
+ export { prerun_default as default };
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
- export { run } from '@oclif/core';
1
+ import { run } from "@oclif/core";
2
+ export { run };
package/dist/index.js CHANGED
@@ -1 +1,3 @@
1
- export { run } from '@oclif/core';
1
+ import { run } from "@oclif/core";
2
+
3
+ export { run };