juo 0.0.1-alpha.3 → 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.
- package/README.md +42 -2
- package/bin/dev.js +1 -1
- package/dist/analytics-B2gwRg5Q.js +69 -0
- package/dist/commands/create.d.ts +15 -0
- package/dist/commands/create.js +465 -0
- package/dist/commands/generate.d.ts +20 -0
- package/dist/commands/generate.js +3 -0
- package/dist/commands/publish/index.d.ts +41 -13
- package/dist/commands/publish/index.js +193 -114
- package/dist/generate-D7J6nPBd.js +252 -0
- package/dist/hooks/postrun.d.ts +6 -0
- package/dist/hooks/postrun.js +62 -0
- package/dist/hooks/prerun.d.ts +6 -0
- package/dist/hooks/prerun.js +15 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -1
- package/oclif.manifest.json +89 -1
- package/package.json +12 -3
- package/templates/.storybook/preview-head.html.liquid +11 -0
- package/templates/block/preact/index.ts.liquid +40 -0
- package/templates/block/preact/{{block.clsName}}.tsx.liquid +200 -0
- package/templates/block/react/index.ts.liquid +41 -0
- package/templates/block/react/state.ts.liquid +105 -0
- package/templates/block/react/{{block.clsName}}.tsx.liquid +91 -0
- package/templates/configFiles/.env.liquid +0 -0
- package/templates/configFiles/.gitignore.liquid +6 -0
- package/templates/configFiles/.prettierignore.liquid +45 -0
- package/templates/configFiles/.prettierrc.liquid +1 -0
- package/templates/configFiles/vite-env.d.ts.liquid +2 -0
- package/templates/css/styles.css +71 -0
- package/templates/stories/{{block.clsName}}.stories.tsx.liquid +24 -0
- package/templates/tailwind/src/tailwind.css.liquid +1 -0
- package/templates/tailwind/vite.config.ts.liquid +23 -0
- package/dist/base-command.d.ts +0 -23
- package/dist/base-command.js +0 -119
|
@@ -1,119 +1,198 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import asyncPool from
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
107
|
+
`<%= config.bin %> publish --theme custom-theme
|
|
16
108
|
Publish a custom theme files.
|
|
17
109
|
`,
|
|
18
|
-
|
|
110
|
+
`<%= config.bin %> publish --reconfigure
|
|
19
111
|
Reconfigure settings and publish.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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,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,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
|
-
|
|
1
|
+
import { run } from "@oclif/core";
|
|
2
|
+
export { run };
|
package/dist/index.js
CHANGED