juo 0.2.0 → 0.3.0-alpha.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/README.md +27 -1
- package/bin/dev.js +7 -0
- package/dist/{analytics-CgHTVQWO.js → analytics-CuhiZN94.js} +5 -5
- package/dist/base-command-BQVnm_gK.d.ts +28 -0
- package/dist/base-command-CqPEFZ4E.js +115 -0
- package/dist/commands/blocks/dev.d.ts +16 -0
- package/dist/commands/blocks/dev.js +71 -0
- package/dist/commands/create.d.ts +4 -4
- package/dist/commands/create.js +15 -3
- package/dist/commands/generate.d.ts +5 -5
- package/dist/commands/generate.js +2 -2
- package/dist/commands/publish/index.d.ts +3 -28
- package/dist/commands/publish/index.js +2 -111
- package/dist/{generate-D9WUAwYz.js → generate-BiHo2p9_.js} +1 -1
- package/dist/hooks/postrun.js +1 -1
- package/dist/hooks/prerun.js +1 -1
- package/dist/{juoTemplateGenerator-Ug40Yo7e.js → juoTemplateGenerator-RkinYN2l.js} +1 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.js +2 -2
- package/dist/{packageGenerator-Dno-ecHh.js → packageGenerator-Bx2FAq2Y.js} +2 -1
- package/oclif.manifest.json +43 -1
- package/package.json +1 -1
- package/templates/block/preact/{{block.clsName}}.tsx.liquid +30 -65
- package/templates/block/react/{{block.clsName}}.tsx.liquid +17 -30
- package/templates/configFiles/vite-env.d.ts.liquid +17 -0
- package/templates/configFiles/vite.config.ts.liquid +3 -2
- package/templates/css/styles.css +2 -14
- package/templates/dev/index.html.liquid +14 -0
- package/templates/dev/src/main.ts.liquid +136 -0
- package/templates/dev/vite/juo-editor-plugin.ts.liquid +43 -0
- package/templates/tailwind/src/tailwind.css.liquid +3 -0
- package/templates/tailwind/vite.config.ts.liquid +3 -2
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ $ npm install -g juo
|
|
|
20
20
|
$ juo COMMAND
|
|
21
21
|
running command...
|
|
22
22
|
$ juo (--version)
|
|
23
|
-
juo/0.
|
|
23
|
+
juo/0.3.0-alpha.0 linux-x64 node-v22.21.0
|
|
24
24
|
$ juo --help [COMMAND]
|
|
25
25
|
USAGE
|
|
26
26
|
$ juo COMMAND
|
|
@@ -31,11 +31,37 @@ USAGE
|
|
|
31
31
|
# Commands
|
|
32
32
|
|
|
33
33
|
<!-- commands -->
|
|
34
|
+
* [`juo blocks dev`](#juo-blocks-dev)
|
|
34
35
|
* [`juo create`](#juo-create)
|
|
35
36
|
* [`juo generate`](#juo-generate)
|
|
36
37
|
* [`juo help [COMMAND]`](#juo-help-command)
|
|
37
38
|
* [`juo publish`](#juo-publish)
|
|
38
39
|
|
|
40
|
+
## `juo blocks dev`
|
|
41
|
+
|
|
42
|
+
Start local block dev server with embedded editor
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
USAGE
|
|
46
|
+
$ juo blocks dev [--reconfigure] [-p <value>]
|
|
47
|
+
|
|
48
|
+
FLAGS
|
|
49
|
+
-p, --port=<value> [default: 5174] Port for the local dev server
|
|
50
|
+
|
|
51
|
+
GLOBAL FLAGS
|
|
52
|
+
--reconfigure Reconfigure settings
|
|
53
|
+
|
|
54
|
+
DESCRIPTION
|
|
55
|
+
Start local block dev server with embedded editor
|
|
56
|
+
|
|
57
|
+
EXAMPLES
|
|
58
|
+
$ juo blocks dev
|
|
59
|
+
Start dev server on default port (5174). Opens editor at http://localhost:5174/editor.
|
|
60
|
+
|
|
61
|
+
$ juo blocks dev --port 3000
|
|
62
|
+
Start dev server on port 3000.
|
|
63
|
+
```
|
|
64
|
+
|
|
39
65
|
## `juo create`
|
|
40
66
|
|
|
41
67
|
Generate a new Juo project
|
package/bin/dev.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
|
|
3
3
|
import {execute} from '@oclif/core'
|
|
4
|
+
import {readFileSync} from 'node:fs'
|
|
5
|
+
import {fileURLToPath} from 'node:url'
|
|
6
|
+
|
|
7
|
+
const editorPkg = JSON.parse(
|
|
8
|
+
readFileSync(fileURLToPath(new URL('../../editor/package.json', import.meta.url)), 'utf8'),
|
|
9
|
+
)
|
|
10
|
+
process.env.BLOCKS_EDITOR_VERSION = editorPkg.version
|
|
4
11
|
|
|
5
12
|
await execute({development: true, dir: import.meta.url})
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import debug from "debug";
|
|
2
2
|
|
|
3
3
|
//#region src/utils/config.ts
|
|
4
|
-
const MIXPANEL_TOKEN = "
|
|
5
|
-
const MIXPANEL_API_URL = "https://api
|
|
6
|
-
const CUSTOMERIO_BETA_FORM_URL = "
|
|
7
|
-
const CUSTOMERIO_SITE_ID = "
|
|
8
|
-
const CUSTOMERIO_API_KEY = "
|
|
4
|
+
const MIXPANEL_TOKEN = "";
|
|
5
|
+
const MIXPANEL_API_URL = "https://api.mixpanel.com";
|
|
6
|
+
const CUSTOMERIO_BETA_FORM_URL = "";
|
|
7
|
+
const CUSTOMERIO_SITE_ID = "";
|
|
8
|
+
const CUSTOMERIO_API_KEY = "";
|
|
9
9
|
|
|
10
10
|
//#endregion
|
|
11
11
|
//#region src/utils/analytics.ts
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Command, Interfaces } from "@oclif/core";
|
|
2
|
+
|
|
3
|
+
//#region src/base-command.d.ts
|
|
4
|
+
interface BaseConfig {
|
|
5
|
+
JUO_API_KEY: string;
|
|
6
|
+
JUO_API_URL: string;
|
|
7
|
+
JUO_TENANT_ID?: string;
|
|
8
|
+
}
|
|
9
|
+
type Flags$1<T extends typeof Command> = Interfaces.InferredFlags<(typeof BaseCommand)['baseFlags'] & T['flags']>;
|
|
10
|
+
type Args<T extends typeof Command> = Interfaces.InferredArgs<T['args']>;
|
|
11
|
+
declare abstract class BaseCommand<T extends typeof Command> extends Command {
|
|
12
|
+
static baseFlags: {
|
|
13
|
+
reconfigure: Interfaces.BooleanFlag<boolean>;
|
|
14
|
+
};
|
|
15
|
+
protected args: Args<T>;
|
|
16
|
+
protected flags: Flags$1<T>;
|
|
17
|
+
protected get configFileName(): string;
|
|
18
|
+
protected get configFilePath(): string;
|
|
19
|
+
protected configExists(): boolean;
|
|
20
|
+
protected ensureConfig(reconfigure?: boolean): Promise<BaseConfig>;
|
|
21
|
+
protected ensureTenantId(): Promise<string>;
|
|
22
|
+
init(): Promise<void>;
|
|
23
|
+
protected loadConfig(): Partial<BaseConfig>;
|
|
24
|
+
protected saveConfig(config: Partial<BaseConfig>): void;
|
|
25
|
+
protected validateConfig(config: Partial<BaseConfig>): config is BaseConfig;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { BaseCommand as t };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Command, Flags } from "@oclif/core";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import inquirer from "inquirer";
|
|
5
|
+
|
|
6
|
+
//#region src/base-command.ts
|
|
7
|
+
var BaseCommand = class extends Command {
|
|
8
|
+
static baseFlags = { reconfigure: Flags.boolean({
|
|
9
|
+
default: false,
|
|
10
|
+
description: "Reconfigure settings",
|
|
11
|
+
helpGroup: "GLOBAL"
|
|
12
|
+
}) };
|
|
13
|
+
args;
|
|
14
|
+
flags;
|
|
15
|
+
get configFileName() {
|
|
16
|
+
return "config.json";
|
|
17
|
+
}
|
|
18
|
+
get configFilePath() {
|
|
19
|
+
return path.join(this.config.configDir, this.configFileName);
|
|
20
|
+
}
|
|
21
|
+
configExists() {
|
|
22
|
+
return fs.existsSync(this.configFilePath);
|
|
23
|
+
}
|
|
24
|
+
async ensureConfig(reconfigure = false) {
|
|
25
|
+
let config = {};
|
|
26
|
+
if (this.configExists()) try {
|
|
27
|
+
config = this.loadConfig();
|
|
28
|
+
if (this.validateConfig(config) && !reconfigure) {
|
|
29
|
+
this.log(`Found configuration at ${this.configFilePath}`);
|
|
30
|
+
return config;
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
this.log(`Warning: Could not parse existing config at ${this.configFilePath}`);
|
|
34
|
+
}
|
|
35
|
+
if (reconfigure && this.validateConfig(config)) this.log("Reconfiguring settings...");
|
|
36
|
+
else this.log("Configuration incomplete. Setting up...");
|
|
37
|
+
const questions = [];
|
|
38
|
+
if (!config.JUO_API_URL || reconfigure) questions.push({
|
|
39
|
+
default: "https://api.juo.io",
|
|
40
|
+
message: "Enter the Juo API URL:",
|
|
41
|
+
name: "JUO_API_URL",
|
|
42
|
+
type: "input",
|
|
43
|
+
validate(input) {
|
|
44
|
+
if (!input.trim()) return "Juo API URL is required";
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
if (!config.JUO_API_KEY || reconfigure) questions.push({
|
|
49
|
+
message: "Enter your Juo API key:",
|
|
50
|
+
name: "JUO_API_KEY",
|
|
51
|
+
type: "password",
|
|
52
|
+
validate(input) {
|
|
53
|
+
if (!input.trim()) return "Juo API key is required";
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
if (questions.length === 0) return config;
|
|
58
|
+
const response = await inquirer.prompt(questions);
|
|
59
|
+
if (response.JUO_API_URL) config.JUO_API_URL = response.JUO_API_URL.trim();
|
|
60
|
+
if (response.JUO_API_KEY) config.JUO_API_KEY = response.JUO_API_KEY.trim();
|
|
61
|
+
this.saveConfig(config);
|
|
62
|
+
this.log(`Saved configuration to ${this.configFilePath}`);
|
|
63
|
+
if (this.validateConfig(config)) return config;
|
|
64
|
+
throw new Error("Failed to create valid configuration");
|
|
65
|
+
}
|
|
66
|
+
async ensureTenantId() {
|
|
67
|
+
let config = {};
|
|
68
|
+
if (this.configExists()) try {
|
|
69
|
+
config = this.loadConfig();
|
|
70
|
+
if (config.JUO_TENANT_ID?.trim()) return config.JUO_TENANT_ID.trim();
|
|
71
|
+
} catch {}
|
|
72
|
+
const tenantId = (await inquirer.prompt([{
|
|
73
|
+
message: "Enter your tenant ID (shop identifier):",
|
|
74
|
+
name: "JUO_TENANT_ID",
|
|
75
|
+
type: "input",
|
|
76
|
+
validate(input) {
|
|
77
|
+
if (!input.trim()) return "Tenant ID is required";
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
}])).JUO_TENANT_ID.trim().replace(/\.myshopify\.com$/, "");
|
|
81
|
+
config.JUO_TENANT_ID = tenantId;
|
|
82
|
+
this.saveConfig(config);
|
|
83
|
+
return tenantId;
|
|
84
|
+
}
|
|
85
|
+
async init() {
|
|
86
|
+
await super.init();
|
|
87
|
+
const { args, flags } = await this.parse({
|
|
88
|
+
args: this.ctor.args,
|
|
89
|
+
baseFlags: super.ctor.baseFlags,
|
|
90
|
+
flags: this.ctor.flags,
|
|
91
|
+
strict: this.ctor.strict
|
|
92
|
+
});
|
|
93
|
+
this.flags = flags;
|
|
94
|
+
this.args = args;
|
|
95
|
+
}
|
|
96
|
+
loadConfig() {
|
|
97
|
+
if (!this.configExists()) throw new Error(`Configuration file not found at ${this.configFilePath}`);
|
|
98
|
+
try {
|
|
99
|
+
const configContent = fs.readFileSync(this.configFilePath, "utf8");
|
|
100
|
+
return JSON.parse(configContent);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throw new Error(`Failed to parse configuration file at ${this.configFilePath}: ${error}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
saveConfig(config) {
|
|
106
|
+
fs.mkdirSync(this.config.configDir, { recursive: true });
|
|
107
|
+
fs.writeFileSync(this.configFilePath, JSON.stringify(config, null, 2), { encoding: "utf8" });
|
|
108
|
+
}
|
|
109
|
+
validateConfig(config) {
|
|
110
|
+
return Boolean(config.JUO_API_KEY && config.JUO_API_KEY.trim().length > 0 && config.JUO_API_URL && config.JUO_API_URL.trim().length > 0);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
//#endregion
|
|
115
|
+
export { BaseCommand as t };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { t as BaseCommand } from "../../base-command-BQVnm_gK.js";
|
|
2
|
+
import * as _oclif_core_interfaces5 from "@oclif/core/interfaces";
|
|
3
|
+
|
|
4
|
+
//#region src/commands/blocks/dev.d.ts
|
|
5
|
+
declare class Dev extends BaseCommand<typeof Dev> {
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
port: _oclif_core_interfaces5.OptionFlag<number, _oclif_core_interfaces5.CustomOptions>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
private resolvePackageJsonPath;
|
|
13
|
+
private tryLoadTenantId;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { Dev as default };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { t as BaseCommand } from "../../base-command-CqPEFZ4E.js";
|
|
2
|
+
import { Flags } from "@oclif/core";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
|
|
7
|
+
//#region src/commands/blocks/dev.ts
|
|
8
|
+
var Dev = class Dev extends BaseCommand {
|
|
9
|
+
static description = "Start local block dev server with embedded editor";
|
|
10
|
+
static examples = [`<%= config.bin %> <%= command.id %>
|
|
11
|
+
Start dev server on default port (5174). Opens editor at http://localhost:5174/editor.
|
|
12
|
+
`, `<%= config.bin %> <%= command.id %> --port 3000
|
|
13
|
+
Start dev server on port 3000.
|
|
14
|
+
`];
|
|
15
|
+
static flags = { port: Flags.integer({
|
|
16
|
+
char: "p",
|
|
17
|
+
default: 5174,
|
|
18
|
+
description: "Port for the local dev server"
|
|
19
|
+
}) };
|
|
20
|
+
async run() {
|
|
21
|
+
const { flags } = await this.parse(Dev);
|
|
22
|
+
const { port } = flags;
|
|
23
|
+
const rootDir = this.resolvePackageJsonPath();
|
|
24
|
+
const tenantId = this.tryLoadTenantId();
|
|
25
|
+
if (tenantId) {
|
|
26
|
+
const devBlocksUrl = `http://localhost:${port}/src/blocks/register.ts`;
|
|
27
|
+
const remoteUrl = `https://admin.juo.io/store/${encodeURIComponent(tenantId)}/editor?dev-blocks-url=${encodeURIComponent(devBlocksUrl)}`;
|
|
28
|
+
this.log(`\n remote editor ${remoteUrl}`);
|
|
29
|
+
}
|
|
30
|
+
const vite = spawn("npx", [
|
|
31
|
+
"vite",
|
|
32
|
+
"--port",
|
|
33
|
+
String(port)
|
|
34
|
+
], {
|
|
35
|
+
cwd: rootDir,
|
|
36
|
+
shell: true,
|
|
37
|
+
stdio: "inherit"
|
|
38
|
+
});
|
|
39
|
+
vite.on("error", (err) => {
|
|
40
|
+
this.error(`Failed to start vite: ${err.message}`);
|
|
41
|
+
});
|
|
42
|
+
const cleanup = () => {
|
|
43
|
+
vite.kill();
|
|
44
|
+
process.exit(0);
|
|
45
|
+
};
|
|
46
|
+
process.on("SIGINT", cleanup);
|
|
47
|
+
process.on("SIGTERM", cleanup);
|
|
48
|
+
await new Promise(() => {});
|
|
49
|
+
}
|
|
50
|
+
resolvePackageJsonPath() {
|
|
51
|
+
let currentPath = path.resolve("./");
|
|
52
|
+
while (!existsSync(path.join(currentPath, "package.json"))) {
|
|
53
|
+
const parentDir = path.dirname(currentPath);
|
|
54
|
+
if (parentDir === currentPath) throw new Error("Could not find 'package.json' in the project");
|
|
55
|
+
currentPath = parentDir;
|
|
56
|
+
}
|
|
57
|
+
return currentPath;
|
|
58
|
+
}
|
|
59
|
+
tryLoadTenantId() {
|
|
60
|
+
if (!this.configExists()) return null;
|
|
61
|
+
try {
|
|
62
|
+
return this.loadConfig().JUO_TENANT_ID?.trim() || null;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
this.warn(`Could not read config file: ${error instanceof Error ? error.message : String(error)}`);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
//#endregion
|
|
71
|
+
export { Dev as default };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Command } from "@oclif/core";
|
|
2
|
-
import * as
|
|
2
|
+
import * as _oclif_core_interfaces9 from "@oclif/core/interfaces";
|
|
3
3
|
|
|
4
4
|
//#region src/commands/create.d.ts
|
|
5
5
|
declare class Create extends Command {
|
|
6
6
|
static description: string;
|
|
7
7
|
static flags: {
|
|
8
|
-
quickstart:
|
|
9
|
-
storybook:
|
|
10
|
-
verbose:
|
|
8
|
+
quickstart: _oclif_core_interfaces9.BooleanFlag<boolean>;
|
|
9
|
+
storybook: _oclif_core_interfaces9.BooleanFlag<boolean>;
|
|
10
|
+
verbose: _oclif_core_interfaces9.BooleanFlag<boolean>;
|
|
11
11
|
};
|
|
12
12
|
run(): Promise<void>;
|
|
13
13
|
private getProjectConfig;
|
package/dist/commands/create.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { r as BaseTemplateGenerator } from "../juoTemplateGenerator-
|
|
2
|
-
import { t as PackageGenerator } from "../packageGenerator-
|
|
3
|
-
import { t as Generate } from "../generate-
|
|
1
|
+
import { r as BaseTemplateGenerator } from "../juoTemplateGenerator-RkinYN2l.js";
|
|
2
|
+
import { t as PackageGenerator } from "../packageGenerator-Bx2FAq2Y.js";
|
|
3
|
+
import { t as Generate } from "../generate-BiHo2p9_.js";
|
|
4
4
|
import { Command, Flags } from "@oclif/core";
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
@@ -16,6 +16,17 @@ var ConfigFilesGenerator = class extends BaseTemplateGenerator {
|
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/utils/devHarnessGenerator.ts
|
|
21
|
+
var DevHarnessGenerator = class extends BaseTemplateGenerator {
|
|
22
|
+
generate(destDir, options) {
|
|
23
|
+
this.renderTemplate("dev", destDir, {
|
|
24
|
+
editorVersion: "0.25.0-alpha.1",
|
|
25
|
+
tailwind: options.tailwind
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
19
30
|
//#endregion
|
|
20
31
|
//#region src/utils/storybookGenerator.ts
|
|
21
32
|
const COMMAND_CONFIG = {
|
|
@@ -262,6 +273,7 @@ var Create = class Create extends Command {
|
|
|
262
273
|
});
|
|
263
274
|
tailwindSpinner.stop("Generated Tailwind CSS configuration");
|
|
264
275
|
}
|
|
276
|
+
new DevHarnessGenerator().generate(projectDir, { tailwind });
|
|
265
277
|
const originalCwd = process.cwd();
|
|
266
278
|
try {
|
|
267
279
|
process.chdir(projectDir);
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { Command } from "@oclif/core";
|
|
2
|
-
import * as
|
|
2
|
+
import * as _oclif_core_interfaces0 from "@oclif/core/interfaces";
|
|
3
3
|
|
|
4
4
|
//#region src/commands/generate.d.ts
|
|
5
5
|
declare class Generate extends Command {
|
|
6
6
|
static description: string;
|
|
7
7
|
static examples: string[];
|
|
8
8
|
static flags: {
|
|
9
|
-
name:
|
|
10
|
-
tailwind:
|
|
11
|
-
type:
|
|
12
|
-
verbose:
|
|
9
|
+
name: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
|
|
10
|
+
tailwind: _oclif_core_interfaces0.BooleanFlag<boolean>;
|
|
11
|
+
type: _oclif_core_interfaces0.OptionFlag<string, _oclif_core_interfaces0.CustomOptions>;
|
|
12
|
+
verbose: _oclif_core_interfaces0.BooleanFlag<boolean>;
|
|
13
13
|
};
|
|
14
14
|
static id: string;
|
|
15
15
|
run(): Promise<void>;
|
|
@@ -1,38 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import * as
|
|
1
|
+
import { t as BaseCommand } from "../../base-command-BQVnm_gK.js";
|
|
2
|
+
import * as _oclif_core_interfaces7 from "@oclif/core/interfaces";
|
|
3
3
|
|
|
4
|
-
//#region src/base-command.d.ts
|
|
5
|
-
interface BaseConfig {
|
|
6
|
-
JUO_API_KEY: string;
|
|
7
|
-
JUO_API_URL: string;
|
|
8
|
-
JUO_TENANT_ID?: string;
|
|
9
|
-
}
|
|
10
|
-
type Flags$1<T extends typeof Command> = Interfaces.InferredFlags<(typeof BaseCommand)['baseFlags'] & T['flags']>;
|
|
11
|
-
type Args<T extends typeof Command> = Interfaces.InferredArgs<T['args']>;
|
|
12
|
-
declare abstract class BaseCommand<T extends typeof Command> extends Command {
|
|
13
|
-
static baseFlags: {
|
|
14
|
-
reconfigure: Interfaces.BooleanFlag<boolean>;
|
|
15
|
-
};
|
|
16
|
-
protected args: Args<T>;
|
|
17
|
-
protected flags: Flags$1<T>;
|
|
18
|
-
protected get configFileName(): string;
|
|
19
|
-
protected get configFilePath(): string;
|
|
20
|
-
protected configExists(): boolean;
|
|
21
|
-
protected ensureConfig(reconfigure?: boolean): Promise<BaseConfig>;
|
|
22
|
-
protected ensureTenantId(): Promise<string>;
|
|
23
|
-
init(): Promise<void>;
|
|
24
|
-
protected loadConfig(): Partial<BaseConfig>;
|
|
25
|
-
protected saveConfig(config: Partial<BaseConfig>): void;
|
|
26
|
-
protected validateConfig(config: Partial<BaseConfig>): config is BaseConfig;
|
|
27
|
-
}
|
|
28
|
-
//#endregion
|
|
29
4
|
//#region src/commands/publish/index.d.ts
|
|
30
5
|
declare class Publish extends BaseCommand<typeof Publish> {
|
|
31
6
|
static args: {};
|
|
32
7
|
static description: string;
|
|
33
8
|
static examples: string[];
|
|
34
9
|
static flags: {
|
|
35
|
-
theme:
|
|
10
|
+
theme: _oclif_core_interfaces7.OptionFlag<string, _oclif_core_interfaces7.CustomOptions>;
|
|
36
11
|
};
|
|
37
12
|
getAllFiles(dirPath: string, basePath: string): string[];
|
|
38
13
|
run(): Promise<void>;
|
|
@@ -1,120 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as BaseCommand } from "../../base-command-CqPEFZ4E.js";
|
|
2
|
+
import { Flags } from "@oclif/core";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
5
6
|
import mime from "mime";
|
|
6
7
|
import asyncPool from "tiny-async-pool";
|
|
7
|
-
import inquirer from "inquirer";
|
|
8
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 ensureTenantId() {
|
|
70
|
-
let config = {};
|
|
71
|
-
if (this.configExists()) try {
|
|
72
|
-
config = this.loadConfig();
|
|
73
|
-
if (config.JUO_TENANT_ID?.trim()) return config.JUO_TENANT_ID.trim();
|
|
74
|
-
} catch {}
|
|
75
|
-
const tenantId = (await inquirer.prompt([{
|
|
76
|
-
message: "Enter your tenant ID (shop identifier):",
|
|
77
|
-
name: "JUO_TENANT_ID",
|
|
78
|
-
type: "input",
|
|
79
|
-
validate(input) {
|
|
80
|
-
if (!input.trim()) return "Tenant ID is required";
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
}])).JUO_TENANT_ID.trim().replace(/\.myshopify\.com$/, "");
|
|
84
|
-
config.JUO_TENANT_ID = tenantId;
|
|
85
|
-
this.saveConfig(config);
|
|
86
|
-
return tenantId;
|
|
87
|
-
}
|
|
88
|
-
async init() {
|
|
89
|
-
await super.init();
|
|
90
|
-
const { args, flags } = await this.parse({
|
|
91
|
-
args: this.ctor.args,
|
|
92
|
-
baseFlags: super.ctor.baseFlags,
|
|
93
|
-
flags: this.ctor.flags,
|
|
94
|
-
strict: this.ctor.strict
|
|
95
|
-
});
|
|
96
|
-
this.flags = flags;
|
|
97
|
-
this.args = args;
|
|
98
|
-
}
|
|
99
|
-
loadConfig() {
|
|
100
|
-
if (!this.configExists()) throw new Error(`Configuration file not found at ${this.configFilePath}`);
|
|
101
|
-
try {
|
|
102
|
-
const configContent = fs.readFileSync(this.configFilePath, "utf8");
|
|
103
|
-
return JSON.parse(configContent);
|
|
104
|
-
} catch (error) {
|
|
105
|
-
throw new Error(`Failed to parse configuration file at ${this.configFilePath}: ${error}`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
saveConfig(config) {
|
|
109
|
-
fs.mkdirSync(this.config.configDir, { recursive: true });
|
|
110
|
-
fs.writeFileSync(this.configFilePath, JSON.stringify(config, null, 2), { encoding: "utf8" });
|
|
111
|
-
}
|
|
112
|
-
validateConfig(config) {
|
|
113
|
-
return Boolean(config.JUO_API_KEY && config.JUO_API_KEY.trim().length > 0 && config.JUO_API_URL && config.JUO_API_URL.trim().length > 0);
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
//#endregion
|
|
118
9
|
//#region src/commands/publish/index.ts
|
|
119
10
|
var Publish = class extends BaseCommand {
|
|
120
11
|
static args = {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as JuoTemplateGenerator } from "./juoTemplateGenerator-
|
|
1
|
+
import { t as JuoTemplateGenerator } from "./juoTemplateGenerator-RkinYN2l.js";
|
|
2
2
|
import { Command, Flags } from "@oclif/core";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
package/dist/hooks/postrun.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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-
|
|
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-CuhiZN94.js";
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { confirm, isCancel, text } from "@clack/prompts";
|
package/dist/hooks/prerun.js
CHANGED
|
@@ -143,6 +143,7 @@ var JuoTemplateGenerator = class extends BaseTemplateGenerator {
|
|
|
143
143
|
});
|
|
144
144
|
if (exports.length === 0) return;
|
|
145
145
|
const registerContent = [
|
|
146
|
+
`import "@juo/customer-ui/web-components";`,
|
|
146
147
|
`import { registerBlock } from "@juo/blocks";`,
|
|
147
148
|
exports.map(({ name, slug }) => `import { ${name} } from "./${slug}";`).join("\n"),
|
|
148
149
|
"",
|
package/dist/lib/index.d.ts
CHANGED
package/dist/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as StoryGenerator, r as BaseTemplateGenerator, t as JuoTemplateGenerator } from "../juoTemplateGenerator-
|
|
2
|
-
import { t as PackageGenerator } from "../packageGenerator-
|
|
1
|
+
import { n as StoryGenerator, r as BaseTemplateGenerator, t as JuoTemplateGenerator } from "../juoTemplateGenerator-RkinYN2l.js";
|
|
2
|
+
import { t as PackageGenerator } from "../packageGenerator-Bx2FAq2Y.js";
|
|
3
3
|
|
|
4
4
|
export { BaseTemplateGenerator, JuoTemplateGenerator, PackageGenerator, StoryGenerator };
|
|
@@ -75,7 +75,7 @@ var PackageGenerator = class {
|
|
|
75
75
|
"build-storybook": "storybook build",
|
|
76
76
|
storybook: "storybook dev -p 6006"
|
|
77
77
|
} : {},
|
|
78
|
-
dev: "vite
|
|
78
|
+
dev: "vite --port 5174",
|
|
79
79
|
preview: "vite preview"
|
|
80
80
|
},
|
|
81
81
|
type: "module",
|
|
@@ -83,6 +83,7 @@ var PackageGenerator = class {
|
|
|
83
83
|
});
|
|
84
84
|
const frameworkConfig = FRAMEWORK_CONFIGS[options.framework];
|
|
85
85
|
baseConfig.dependencies = {
|
|
86
|
+
"@juo/customer-ui": "^0.5.0-alpha.4",
|
|
86
87
|
...baseConfig.dependencies,
|
|
87
88
|
...frameworkConfig.dependencies
|
|
88
89
|
};
|
package/oclif.manifest.json
CHANGED
|
@@ -93,6 +93,48 @@
|
|
|
93
93
|
"generate.js"
|
|
94
94
|
]
|
|
95
95
|
},
|
|
96
|
+
"blocks:dev": {
|
|
97
|
+
"aliases": [],
|
|
98
|
+
"args": {},
|
|
99
|
+
"description": "Start local block dev server with embedded editor",
|
|
100
|
+
"examples": [
|
|
101
|
+
"<%= config.bin %> <%= command.id %>\nStart dev server on default port (5174). Opens editor at http://localhost:5174/editor.\n",
|
|
102
|
+
"<%= config.bin %> <%= command.id %> --port 3000\nStart dev server on port 3000.\n"
|
|
103
|
+
],
|
|
104
|
+
"flags": {
|
|
105
|
+
"reconfigure": {
|
|
106
|
+
"description": "Reconfigure settings",
|
|
107
|
+
"helpGroup": "GLOBAL",
|
|
108
|
+
"name": "reconfigure",
|
|
109
|
+
"allowNo": false,
|
|
110
|
+
"type": "boolean"
|
|
111
|
+
},
|
|
112
|
+
"port": {
|
|
113
|
+
"char": "p",
|
|
114
|
+
"description": "Port for the local dev server",
|
|
115
|
+
"name": "port",
|
|
116
|
+
"default": 5174,
|
|
117
|
+
"hasDynamicHelp": false,
|
|
118
|
+
"multiple": false,
|
|
119
|
+
"type": "option"
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"hasDynamicHelp": false,
|
|
123
|
+
"hiddenAliases": [],
|
|
124
|
+
"id": "blocks:dev",
|
|
125
|
+
"pluginAlias": "juo",
|
|
126
|
+
"pluginName": "juo",
|
|
127
|
+
"pluginType": "core",
|
|
128
|
+
"strict": true,
|
|
129
|
+
"enableJsonFlag": false,
|
|
130
|
+
"isESM": true,
|
|
131
|
+
"relativePath": [
|
|
132
|
+
"dist",
|
|
133
|
+
"commands",
|
|
134
|
+
"blocks",
|
|
135
|
+
"dev.js"
|
|
136
|
+
]
|
|
137
|
+
},
|
|
96
138
|
"publish": {
|
|
97
139
|
"aliases": [],
|
|
98
140
|
"args": {},
|
|
@@ -137,5 +179,5 @@
|
|
|
137
179
|
]
|
|
138
180
|
}
|
|
139
181
|
},
|
|
140
|
-
"version": "0.
|
|
182
|
+
"version": "0.3.0-alpha.0"
|
|
141
183
|
}
|
package/package.json
CHANGED
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
import { useState } from "preact/hooks";
|
|
2
2
|
import type { Props } from "./index.ts";{% if block.tailwind %}
|
|
3
3
|
|
|
4
|
-
const Counter = ({
|
|
5
|
-
className,
|
|
6
|
-
initialCount,
|
|
7
|
-
}: {
|
|
8
|
-
className: string;
|
|
9
|
-
initialCount: number;
|
|
10
|
-
}) => {
|
|
11
|
-
const [count, setCount] = useState(initialCount);
|
|
12
|
-
return (
|
|
13
|
-
<button
|
|
14
|
-
className={`px-4 py-2 rounded-sm cursor-pointer ${className}`}
|
|
15
|
-
onClick={() => setCount(count + 1)}
|
|
16
|
-
>
|
|
17
|
-
count: is {count}
|
|
18
|
-
</button>
|
|
19
|
-
);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
4
|
const FrameworkIcon = ({ className }: { className: string }) => {
|
|
23
5
|
return (
|
|
24
6
|
<svg
|
|
@@ -77,47 +59,30 @@ const TypeScriptIcon = ({ className }: { className: string }) => {
|
|
|
77
59
|
};
|
|
78
60
|
|
|
79
61
|
export function {{ block.clsName }}(props: Props) {
|
|
62
|
+
const [count, setCount] = useState(props.initialCount);
|
|
80
63
|
return (
|
|
81
|
-
<
|
|
82
|
-
<div className="flex items-center
|
|
83
|
-
<div className="flex items-center gap-
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
64
|
+
<juo-card>
|
|
65
|
+
<div className="font-[540] flex gap-8 justify-between items-center py-10 px-10">
|
|
66
|
+
<div className="flex items-center gap-[14px]">
|
|
67
|
+
<div className="flex items-center gap-2">
|
|
68
|
+
<FrameworkIcon className="w-9 h-9 fill-current text-black" />
|
|
69
|
+
<TypeScriptIcon className="w-10 h-10 fill-current text-black" />
|
|
70
|
+
</div>
|
|
71
|
+
<div className="flex flex-col self-center">
|
|
72
|
+
<p className="text-[12px] leading-4 text-[#696969]">
|
|
73
|
+
{props.subtitle}
|
|
74
|
+
</p>
|
|
75
|
+
<p className="text-[15px] leading-none text-[#111]">{props.title}</p>
|
|
76
|
+
</div>
|
|
92
77
|
</div>
|
|
78
|
+
<juo-button onClick={() => setCount(count + 1)}>count: is {count}</juo-button>
|
|
93
79
|
</div>
|
|
94
|
-
|
|
95
|
-
className="text-xs leading-3 text-white bg-[#111] rounded-md px-2 py-1.5 min-w-22"
|
|
96
|
-
initialCount={props.initialCount}
|
|
97
|
-
/>
|
|
98
|
-
</div>
|
|
80
|
+
</juo-card>
|
|
99
81
|
);
|
|
100
82
|
}
|
|
101
83
|
|
|
102
84
|
{% else %}
|
|
103
85
|
import "../../styles.css";
|
|
104
|
-
const Counter = ({
|
|
105
|
-
className,
|
|
106
|
-
initialCount,
|
|
107
|
-
}: {
|
|
108
|
-
className: string;
|
|
109
|
-
initialCount: number;
|
|
110
|
-
}) => {
|
|
111
|
-
const [count, setCount] = useState(initialCount);
|
|
112
|
-
return (
|
|
113
|
-
<button
|
|
114
|
-
className={`counter-button ${className}`}
|
|
115
|
-
onClick={() => setCount(count + 1)}
|
|
116
|
-
>
|
|
117
|
-
count: is {count}
|
|
118
|
-
</button>
|
|
119
|
-
);
|
|
120
|
-
};
|
|
121
86
|
|
|
122
87
|
const FrameworkIcon = ({ className }: { className: string }) => {
|
|
123
88
|
return (
|
|
@@ -177,23 +142,23 @@ const TypeScriptIcon = ({ className }: { className: string }) => {
|
|
|
177
142
|
};
|
|
178
143
|
|
|
179
144
|
export function {{ block.clsName }}(props: Props) {
|
|
145
|
+
const [count, setCount] = useState(props.initialCount);
|
|
180
146
|
return (
|
|
181
|
-
<
|
|
182
|
-
<div className="
|
|
183
|
-
<div className="
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
<
|
|
189
|
-
|
|
147
|
+
<juo-card>
|
|
148
|
+
<div className="container">
|
|
149
|
+
<div className="content">
|
|
150
|
+
<div className="icon-container">
|
|
151
|
+
<FrameworkIcon className="framework-icon" />
|
|
152
|
+
<TypeScriptIcon className="typescript-icon" />
|
|
153
|
+
</div>
|
|
154
|
+
<div className="text-container">
|
|
155
|
+
<p className="subtitle">{props.subtitle}</p>
|
|
156
|
+
<p className="title">{props.title}</p>
|
|
157
|
+
</div>
|
|
190
158
|
</div>
|
|
159
|
+
<juo-button onClick={() => setCount(count + 1)}>count: is {count}</juo-button>
|
|
191
160
|
</div>
|
|
192
|
-
|
|
193
|
-
className="counter-button-custom"
|
|
194
|
-
initialCount={props.initialCount}
|
|
195
|
-
/>
|
|
196
|
-
</div>
|
|
161
|
+
</juo-card>
|
|
197
162
|
);
|
|
198
163
|
}
|
|
199
164
|
{% endif %}
|
|
@@ -1,19 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useState } from "react";
|
|
2
2
|
import { MantineProvider } from "@mantine/core";
|
|
3
3
|
import type { Props } from ".";
|
|
4
4
|
|
|
5
|
-
const Counter = ({ className, initialCount = 0 }: { className: string; initialCount?: number }) => {
|
|
6
|
-
const [count, setCount] = useState(initialCount);
|
|
7
|
-
return (
|
|
8
|
-
<button
|
|
9
|
-
className={`px-4 py-2 rounded-md cursor-pointer ${className}`}
|
|
10
|
-
onClick={() => setCount(count + 1)}
|
|
11
|
-
>
|
|
12
|
-
count: is {count}
|
|
13
|
-
</button>
|
|
14
|
-
);
|
|
15
|
-
};
|
|
16
|
-
|
|
17
5
|
const FrameworkIcon = ({ className }: { className: string }) => {
|
|
18
6
|
return (
|
|
19
7
|
<svg
|
|
@@ -63,28 +51,27 @@ const TypeScriptIcon = ({ className }: { className: string }) => {
|
|
|
63
51
|
};
|
|
64
52
|
|
|
65
53
|
export function {{ block.clsName }}(props: Props) {
|
|
66
|
-
const
|
|
54
|
+
const [count, setCount] = useState(props.initialCount ?? 0);
|
|
67
55
|
|
|
68
56
|
return (
|
|
69
57
|
<MantineProvider>
|
|
70
|
-
<
|
|
71
|
-
<div className="flex items-center
|
|
72
|
-
<div className="flex items-center gap-
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
58
|
+
<juo-card>
|
|
59
|
+
<div className="font-[540] flex gap-8 justify-between items-center py-10 px-10">
|
|
60
|
+
<div className="flex items-center gap-[14px]">
|
|
61
|
+
<div className="flex items-center gap-2">
|
|
62
|
+
<FrameworkIcon className="w-9 h-9 fill-current text-black" />
|
|
63
|
+
<TypeScriptIcon className="w-10 h-10 fill-current text-black" />
|
|
64
|
+
</div>
|
|
65
|
+
<div className="flex flex-col self-center">
|
|
66
|
+
<p className="text-[12px] leading-4 text-[#696969]">
|
|
67
|
+
{props.subtitle}
|
|
68
|
+
</p>
|
|
69
|
+
<p className="text-[15px] leading-none text-[#111]">{props.title}</p>
|
|
70
|
+
</div>
|
|
81
71
|
</div>
|
|
72
|
+
<juo-button onClick={() => setCount(count + 1)}>count: is {count}</juo-button>
|
|
82
73
|
</div>
|
|
83
|
-
|
|
84
|
-
className="text-xs leading-3 text-white bg-[#111] rounded-md px-2 py-1.5 min-w-22"
|
|
85
|
-
initialCount={props.initialCount}
|
|
86
|
-
/>
|
|
87
|
-
</div>
|
|
74
|
+
</juo-card>
|
|
88
75
|
</MantineProvider>
|
|
89
76
|
);
|
|
90
77
|
}
|
|
@@ -1,2 +1,19 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
2
|
/// <reference types="vite-plugin-svgr/client" />
|
|
3
|
+
{% if framework == "react" %}
|
|
4
|
+
|
|
5
|
+
declare namespace React {
|
|
6
|
+
namespace JSX {
|
|
7
|
+
// Bridge HTMLElementTagNameMap (augmented by @juo/customer-ui/web-components) into
|
|
8
|
+
// React's IntrinsicElements so that juo-* custom elements typecheck without a
|
|
9
|
+
// hardcoded list. Omit<…, keyof HTMLElement> strips DOM methods/properties,
|
|
10
|
+
// leaving only component-specific props.
|
|
11
|
+
type _JuoElements = {
|
|
12
|
+
[K in Extract<keyof HTMLElementTagNameMap, `juo-${string}`>]:
|
|
13
|
+
React.HTMLAttributes<HTMLElementTagNameMap[K]> &
|
|
14
|
+
Partial<Omit<HTMLElementTagNameMap[K], keyof HTMLElement>>;
|
|
15
|
+
};
|
|
16
|
+
interface IntrinsicElements extends _JuoElements {}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
{% endif %}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineConfig } from "vite";
|
|
2
2
|
import svgr from "vite-plugin-svgr";
|
|
3
|
+
import juo from "./vite/juo-editor-plugin";
|
|
3
4
|
{% if framework == "react" %}
|
|
4
5
|
import react from "@vitejs/plugin-react";
|
|
5
6
|
{% elsif framework == "preact" %}
|
|
@@ -10,7 +11,7 @@ export default defineConfig({
|
|
|
10
11
|
define: {
|
|
11
12
|
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
|
|
12
13
|
},
|
|
13
|
-
plugins: [{% if framework == "react" %}react(), {% endif %}{% if framework == "preact" %}preact(), {% endif %}
|
|
14
|
+
plugins: [juo(), {% if framework == "react" %}react(), {% endif %}{% if framework == "preact" %}preact(), {% endif %}
|
|
14
15
|
svgr({
|
|
15
16
|
svgrOptions: {
|
|
16
17
|
icon: true,
|
|
@@ -27,7 +28,7 @@ export default defineConfig({
|
|
|
27
28
|
fileName: "index",
|
|
28
29
|
},
|
|
29
30
|
rollupOptions: {
|
|
30
|
-
external: [
|
|
31
|
+
external: [/^@juo\/blocks/],
|
|
31
32
|
},
|
|
32
33
|
},
|
|
33
34
|
});
|
package/templates/css/styles.css
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
.
|
|
2
|
-
|
|
3
|
-
border-radius: 0.125rem;
|
|
4
|
-
cursor: pointer;
|
|
5
|
-
}
|
|
1
|
+
@import "@juo/customer-ui/design-tokens.css";
|
|
2
|
+
@import "@juo/customer-ui/theme-modes.css";
|
|
6
3
|
|
|
7
4
|
.container {
|
|
8
5
|
font-weight: 540;
|
|
@@ -60,12 +57,3 @@
|
|
|
60
57
|
color: #111;
|
|
61
58
|
}
|
|
62
59
|
|
|
63
|
-
.counter-button-custom {
|
|
64
|
-
font-size: 0.75rem;
|
|
65
|
-
line-height: 0.75rem;
|
|
66
|
-
color: white;
|
|
67
|
-
background-color: #111;
|
|
68
|
-
border-radius: 0.375rem;
|
|
69
|
-
padding: 0.375rem 0.5rem;
|
|
70
|
-
min-width: 5.5rem;
|
|
71
|
-
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Juo Blocks Dev</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<juo-context-root>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
</juo-context-root>
|
|
12
|
+
<script type="module" src="/src/main.ts"></script>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import "@juo/blocks/web-components/editor";
|
|
2
|
+
{% if tailwind %}import "./tailwind.css";
|
|
3
|
+
{% else %}import "./styles.css";
|
|
4
|
+
{% endif %}
|
|
5
|
+
import {
|
|
6
|
+
createBlockInstance,
|
|
7
|
+
createThemeState,
|
|
8
|
+
defineBlock,
|
|
9
|
+
effect,
|
|
10
|
+
injectContext,
|
|
11
|
+
isEditorPreview,
|
|
12
|
+
provideContext,
|
|
13
|
+
registerBlock,
|
|
14
|
+
serializeBlocks,
|
|
15
|
+
ThemeStateContext,
|
|
16
|
+
TranslationContext,
|
|
17
|
+
createTranslationService,
|
|
18
|
+
untracked,
|
|
19
|
+
} from "@juo/blocks";
|
|
20
|
+
import { registerBlocks as registerProjectBlocks } from "./blocks/register";
|
|
21
|
+
|
|
22
|
+
const homePageBlock = defineBlock("HomePage", {
|
|
23
|
+
displayName: "Home page",
|
|
24
|
+
group: "page",
|
|
25
|
+
schema: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
props: {
|
|
29
|
+
type: "object",
|
|
30
|
+
properties: {},
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
},
|
|
33
|
+
slots: {
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: { main: {} },
|
|
36
|
+
additionalProperties: false,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
required: ["props", "slots"],
|
|
40
|
+
additionalProperties: false,
|
|
41
|
+
},
|
|
42
|
+
initialValue: () => ({
|
|
43
|
+
props: {},
|
|
44
|
+
slots: {
|
|
45
|
+
main: [createBlockInstance("Starter Block")],
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
renderer() {
|
|
49
|
+
const page = document.createElement("juo-page");
|
|
50
|
+
page.setAttribute("name", "HomePage");
|
|
51
|
+
const extensionRoot = document.createElement("juo-extension-root");
|
|
52
|
+
extensionRoot.setAttribute("name", "main");
|
|
53
|
+
page.appendChild(extensionRoot);
|
|
54
|
+
return page;
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
function registerBlocks() {
|
|
59
|
+
registerProjectBlocks();
|
|
60
|
+
registerBlock(homePageBlock);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function setupEditorMode(root: HTMLElement) {
|
|
64
|
+
const editorModule = await import("@juo/blocks/editor");
|
|
65
|
+
const editor = editorModule.createEditorBridge();
|
|
66
|
+
const routerService = {
|
|
67
|
+
push: () => {},
|
|
68
|
+
subscribe: () => () => {},
|
|
69
|
+
getPages: () => [{ path: "/", block: "HomePage", title: "Home", routeNames: [] }],
|
|
70
|
+
};
|
|
71
|
+
const themeState = createThemeState({
|
|
72
|
+
resolve: (surface, path) => editor.requestThemeState(surface, path),
|
|
73
|
+
upsertState: (surface, path, blocks) =>
|
|
74
|
+
editor.upsertThemeState(surface, path, blocks),
|
|
75
|
+
translations: {
|
|
76
|
+
resolveTranslations: (locale) =>
|
|
77
|
+
editor.requestTranslationOverrides(locale),
|
|
78
|
+
},
|
|
79
|
+
routerService,
|
|
80
|
+
});
|
|
81
|
+
provideContext(root, ThemeStateContext, themeState);
|
|
82
|
+
await editorModule.setupEditorMode({ routerService, themeState });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function setupViewMode(root: HTMLElement) {
|
|
86
|
+
const themeState = createThemeState({
|
|
87
|
+
resolve: async () => ({
|
|
88
|
+
blocks: serializeBlocks([createBlockInstance("HomePage")]),
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
provideContext(root, ThemeStateContext, themeState);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function initializeTheme() {
|
|
95
|
+
const root = document.querySelector("juo-context-root") as HTMLElement;
|
|
96
|
+
|
|
97
|
+
registerBlocks();
|
|
98
|
+
|
|
99
|
+
if (isEditorPreview()) {
|
|
100
|
+
await setupEditorMode(root);
|
|
101
|
+
} else {
|
|
102
|
+
setupViewMode(root);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const themeState = injectContext(root, ThemeStateContext);
|
|
106
|
+
|
|
107
|
+
await themeState.locales.setLocale("en");
|
|
108
|
+
provideContext(
|
|
109
|
+
root,
|
|
110
|
+
TranslationContext,
|
|
111
|
+
createTranslationService(themeState.locales, {
|
|
112
|
+
async loadDefaultTranslations() {
|
|
113
|
+
return {};
|
|
114
|
+
},
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
void themeState.resolve("page", "/");
|
|
119
|
+
|
|
120
|
+
effect(() => {
|
|
121
|
+
const block = themeState.page.blocks.value.at(0);
|
|
122
|
+
untracked(() => {
|
|
123
|
+
if (block == null) {
|
|
124
|
+
document.querySelector("#app")?.replaceChildren();
|
|
125
|
+
} else {
|
|
126
|
+
document.querySelector("#app")?.replaceChildren(block.definition.render(block));
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (document.readyState === "loading") {
|
|
133
|
+
document.addEventListener("DOMContentLoaded", () => void initializeTheme());
|
|
134
|
+
} else {
|
|
135
|
+
void initializeTheme();
|
|
136
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
|
|
3
|
+
const EDITOR_EMBED_URL =
|
|
4
|
+
"https://cdn.juo.io/web/blocks/juo/editor/{{ editorVersion }}/juo-editor.embed.js";
|
|
5
|
+
|
|
6
|
+
export default function juo(): Plugin {
|
|
7
|
+
return {
|
|
8
|
+
name: "juo-editor",
|
|
9
|
+
configureServer(server) {
|
|
10
|
+
server.middlewares.use("/editor", (_req, res) => {
|
|
11
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
12
|
+
res.end(`<!DOCTYPE html>
|
|
13
|
+
<html lang="en">
|
|
14
|
+
<head>
|
|
15
|
+
<meta charset="UTF-8" />
|
|
16
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
17
|
+
<title>Juo Blocks Editor</title>
|
|
18
|
+
<style>
|
|
19
|
+
html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; }
|
|
20
|
+
juo-editor { display: block; height: 100vh; }
|
|
21
|
+
</style>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
<script type="module" src="${EDITOR_EMBED_URL}"></script>
|
|
25
|
+
<juo-editor url="/?editor=true"></juo-editor>
|
|
26
|
+
</body>
|
|
27
|
+
</html>`);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
server.httpServer?.once("listening", () => {
|
|
31
|
+
const address = server.httpServer?.address();
|
|
32
|
+
const port =
|
|
33
|
+
address && typeof address === "object" ? address.port : 5174;
|
|
34
|
+
const base = `http://localhost:${port}`;
|
|
35
|
+
server.config.logger.info(
|
|
36
|
+
`\n juo editor ${base}/editor` +
|
|
37
|
+
`\n editor view mode ${base}/` +
|
|
38
|
+
`\n blocks bundle ${base}/src/blocks/register.ts\n`,
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineConfig } from "vite";
|
|
2
2
|
import tailwindcss from "@tailwindcss/vite";
|
|
3
3
|
import svgr from "vite-plugin-svgr";
|
|
4
|
+
import juo from "./vite/juo-editor-plugin";
|
|
4
5
|
{% if framework == "react" %}
|
|
5
6
|
import react from "@vitejs/plugin-react";
|
|
6
7
|
{% elsif framework == "preact" %}
|
|
@@ -11,7 +12,7 @@ export default defineConfig({
|
|
|
11
12
|
define: {
|
|
12
13
|
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
|
|
13
14
|
},
|
|
14
|
-
plugins: [tailwindcss(){% if framework == "react" %}, react(){% endif %}{% if framework == "preact" %}, preact(){% endif %},
|
|
15
|
+
plugins: [tailwindcss(), juo(){% if framework == "react" %}, react(){% endif %}{% if framework == "preact" %}, preact(){% endif %},
|
|
15
16
|
svgr({
|
|
16
17
|
svgrOptions: {
|
|
17
18
|
icon: true,
|
|
@@ -28,7 +29,7 @@ export default defineConfig({
|
|
|
28
29
|
fileName: "index",
|
|
29
30
|
},
|
|
30
31
|
rollupOptions: {
|
|
31
|
-
external: [
|
|
32
|
+
external: [/^@juo\/blocks/],
|
|
32
33
|
},
|
|
33
34
|
},
|
|
34
35
|
});
|