juo 0.3.0-alpha.1 → 0.3.0-alpha.3

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 CHANGED
@@ -20,7 +20,7 @@ $ npm install -g juo
20
20
  $ juo COMMAND
21
21
  running command...
22
22
  $ juo (--version)
23
- juo/0.3.0-alpha.1 linux-x64 node-v22.21.0
23
+ juo/0.3.0-alpha.3 linux-x64 node-v22.21.0
24
24
  $ juo --help [COMMAND]
25
25
  USAGE
26
26
  $ juo COMMAND
package/bin/dev.js CHANGED
@@ -11,6 +11,7 @@ import {fileURLToPath} from 'node:url'
11
11
  const readVersion = (relPath) =>
12
12
  JSON.parse(readFileSync(fileURLToPath(new URL(relPath, import.meta.url)), 'utf8')).version
13
13
 
14
+ process.env.BLOCKS_DEVTOOLS_VERSION = readVersion('../../devtools/overlay/package.json')
14
15
  process.env.BLOCKS_EDITOR_VERSION = readVersion('../../editor/package.json')
15
16
  process.env.BLOCKS_EXTENSIONS_VERSION = readVersion('../../extensions/package.json')
16
17
  // `__CUSTOMER_UI_VERSION__` / `__BLOCKS_VERSION__` are bare globals in
@@ -1,12 +1,12 @@
1
1
  import { t as BaseCommand } from "../../base-command-BQVnm_gK.js";
2
- import * as _oclif_core_interfaces0 from "@oclif/core/interfaces";
2
+ import * as _oclif_core_interfaces10 from "@oclif/core/interfaces";
3
3
 
4
4
  //#region src/commands/blocks/dev.d.ts
5
5
  declare class Dev extends BaseCommand<typeof Dev> {
6
6
  static description: string;
7
7
  static examples: string[];
8
8
  static flags: {
9
- port: _oclif_core_interfaces0.OptionFlag<number, _oclif_core_interfaces0.CustomOptions>;
9
+ port: _oclif_core_interfaces10.OptionFlag<number, _oclif_core_interfaces10.CustomOptions>;
10
10
  };
11
11
  run(): Promise<void>;
12
12
  private resolvePackageJsonPath;
@@ -1,13 +1,13 @@
1
1
  import { Command } from "@oclif/core";
2
- import * as _oclif_core_interfaces1 from "@oclif/core/interfaces";
2
+ import * as _oclif_core_interfaces0 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: _oclif_core_interfaces1.BooleanFlag<boolean>;
9
- storybook: _oclif_core_interfaces1.BooleanFlag<boolean>;
10
- verbose: _oclif_core_interfaces1.BooleanFlag<boolean>;
8
+ quickstart: _oclif_core_interfaces0.BooleanFlag<boolean>;
9
+ storybook: _oclif_core_interfaces0.BooleanFlag<boolean>;
10
+ verbose: _oclif_core_interfaces0.BooleanFlag<boolean>;
11
11
  };
12
12
  run(): Promise<void>;
13
13
  private getProjectConfig;
@@ -1,5 +1,5 @@
1
1
  import { r as BaseTemplateGenerator } from "../juoTemplateGenerator-RkinYN2l.js";
2
- import { t as PackageGenerator } from "../packageGenerator-D81lWXTA.js";
2
+ import { t as PackageGenerator } from "../packageGenerator-CE68rONC.js";
3
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";
@@ -7,7 +7,7 @@ import path from "node:path";
7
7
  import { spawn } from "node:child_process";
8
8
  import { defineTSConfig, writeTSConfig } from "pkg-types";
9
9
  import { confirm, isCancel, outro, select, spinner, text } from "@clack/prompts";
10
- import { readdir, rm, stat, unlink } from "node:fs/promises";
10
+ import { rm } from "node:fs/promises";
11
11
 
12
12
  //#region src/utils/configFilesGenerator.ts
13
13
  var ConfigFilesGenerator = class extends BaseTemplateGenerator {
@@ -21,8 +21,9 @@ var ConfigFilesGenerator = class extends BaseTemplateGenerator {
21
21
  var DevHarnessGenerator = class extends BaseTemplateGenerator {
22
22
  generate(destDir, options) {
23
23
  this.renderTemplate("dev", destDir, {
24
- editorVersion: "0.25.0-alpha.2",
25
- extensionsVersion: "1.12.0-alpha.1",
24
+ devtoolsVersion: "0.1.0-alpha.3",
25
+ editorVersion: "0.25.0-alpha.4",
26
+ extensionsVersion: "1.12.0-alpha.3",
26
27
  tailwind: options.tailwind
27
28
  });
28
29
  }
@@ -37,24 +38,8 @@ const COMMAND_CONFIG = {
37
38
  };
38
39
  var StorybookGenerator = class extends BaseTemplateGenerator {
39
40
  async appendStylesToPreview(destDir) {
40
- const storybookDir = path.join(destDir, ".storybook");
41
- const previewJsPath = path.join(storybookDir, "preview.js");
42
- const previewTsPath = path.join(storybookDir, "preview.ts");
43
- const importStatement = "import \"../src/styles.css\";\n";
44
41
  try {
45
- const previewJsExists = existsSync(previewJsPath);
46
- const previewTsExists = existsSync(previewTsPath);
47
- let targetPath;
48
- if (previewTsExists) targetPath = previewTsPath;
49
- else if (previewJsExists) targetPath = previewJsPath;
50
- else {
51
- targetPath = previewTsPath;
52
- mkdirSync(storybookDir, { recursive: true });
53
- writeFileSync(targetPath, importStatement);
54
- return;
55
- }
56
- const existingContent = readFileSync(targetPath, "utf8");
57
- if (!existingContent.includes("import \"../src/styles.css\"")) writeFileSync(targetPath, importStatement + existingContent);
42
+ this.prependImportToPreview(destDir, "import \"../src/styles.css\";");
58
43
  } catch (error) {
59
44
  throw new Error(`Error appending styles to preview: ${error}`);
60
45
  }
@@ -106,33 +91,94 @@ var StorybookGenerator = class extends BaseTemplateGenerator {
106
91
  const storybookDir = path.join(destDir, ".storybook");
107
92
  this.renderTemplate(".storybook", storybookDir, {});
108
93
  }
109
- async cleanUp(destDir) {
110
- try {
111
- const items = await readdir(destDir);
112
- const itemsToSkip = new Set(["assets", "Configure.mdx"]);
113
- for (const item of items) {
114
- const itemPath = path.join(destDir, item);
115
- const stats = await stat(itemPath);
116
- if (itemsToSkip.has(item)) continue;
117
- await (stats.isDirectory() ? rm(itemPath, {
118
- force: true,
119
- recursive: true
120
- }) : unlink(itemPath));
121
- }
122
- } catch (error) {
123
- throw new Error(`Error cleaning up storybook: ${error}`);
94
+ /**
95
+ * Prepends an import to the Storybook preview file (preview.ts/preview.js),
96
+ * creating it if Storybook init didn't. No-ops when the import is already
97
+ * present so repeated runs stay idempotent.
98
+ */
99
+ prependImportToPreview(destDir, importStatement) {
100
+ const storybookDir = path.join(destDir, ".storybook");
101
+ const previewTsPath = path.join(storybookDir, "preview.ts");
102
+ const previewJsPath = path.join(storybookDir, "preview.js");
103
+ const line = `${importStatement}\n`;
104
+ let targetPath;
105
+ if (existsSync(previewTsPath)) targetPath = previewTsPath;
106
+ else if (existsSync(previewJsPath)) targetPath = previewJsPath;
107
+ else {
108
+ mkdirSync(storybookDir, { recursive: true });
109
+ writeFileSync(previewTsPath, line);
110
+ return;
124
111
  }
112
+ const existingContent = readFileSync(targetPath, "utf8");
113
+ if (!existingContent.includes(importStatement)) writeFileSync(targetPath, line + existingContent);
114
+ }
115
+ /**
116
+ * The starter block renders `@juo/customer-ui` web components (`<juo-card>`,
117
+ * `<juo-text>`, `<juo-button>`). The dev harness registers them via the
118
+ * generated `register.ts`, but the Storybook preview has no such entry, so
119
+ * import the side-effecting bundle here to define the custom elements.
120
+ */
121
+ registerWebComponents(destDir) {
122
+ this.prependImportToPreview(destDir, "import \"@juo/customer-ui/web-components\";");
123
+ }
124
+ injectViteFinal(content, isTs) {
125
+ const viteFinal = [
126
+ " viteFinal: async (config) => {",
127
+ " // Storybook loads the project vite.config, which registers the dev-harness",
128
+ " // `juo()` plugin. That plugin injects the Blocks editor/devtools embed into",
129
+ " // every served HTML page, so strip it from the preview pipeline here.",
130
+ " config.plugins = (config.plugins ?? []).filter(",
131
+ ` (plugin) => !(plugin && typeof plugin === "object" && ${isTs ? "(plugin as { name?: string }).name === \"juo-editor\"" : "plugin.name === \"juo-editor\""}),`,
132
+ " );",
133
+ " return config;",
134
+ " },"
135
+ ].join("\n");
136
+ for (const pattern of [
137
+ /(const config\s*:\s*StorybookConfig\s*=\s*\{)/,
138
+ /(defineMain\(\{)/,
139
+ /(export default \{)/
140
+ ]) if (pattern.test(content)) return content.replace(pattern, `$1\n${viteFinal}`);
141
+ return content;
142
+ }
143
+ /**
144
+ * Rewrites the Storybook init `main.ts`/`main.js` so it (1) discovers stories
145
+ * from the top-level `stories/` dir the CLI emits block stories into, and
146
+ * (2) strips the dev-harness `juo()` plugin from the preview's vite pipeline.
147
+ */
148
+ async patchMainConfig(destDir) {
149
+ const storybookDir = path.join(destDir, ".storybook");
150
+ const tsPath = path.join(storybookDir, "main.ts");
151
+ const jsPath = path.join(storybookDir, "main.js");
152
+ const mainPath = existsSync(tsPath) ? tsPath : existsSync(jsPath) ? jsPath : null;
153
+ if (!mainPath) return;
154
+ let content = readFileSync(mainPath, "utf8");
155
+ content = content.replaceAll("../src/**", "../stories/**");
156
+ if (!content.includes("viteFinal")) content = this.injectViteFinal(content, mainPath.endsWith(".ts"));
157
+ writeFileSync(mainPath, content);
125
158
  }
126
159
  async postGenerate(destDir) {
127
- const storiesDir = path.join(destDir, "stories");
128
160
  try {
129
- if (!existsSync(storiesDir)) mkdirSync(storiesDir, { recursive: true });
130
- await this.cleanUp(storiesDir);
161
+ await this.removeExampleStories(destDir);
131
162
  await this.appendPreviewHead(destDir);
163
+ await this.patchMainConfig(destDir);
164
+ this.registerWebComponents(destDir);
132
165
  } catch (error) {
133
166
  throw new Error(`Error post-generating storybook: ${error}`);
134
167
  }
135
168
  }
169
+ /**
170
+ * Storybook init scaffolds example stories (Button/Header/Page) into
171
+ * `src/stories`. The CLI emits real block stories into the top-level
172
+ * `stories/` dir afterwards, so drop the scaffolding to avoid shipping
173
+ * unrelated example stories.
174
+ */
175
+ async removeExampleStories(destDir) {
176
+ const exampleDirs = [path.join(destDir, "src", "stories"), path.join(destDir, "stories")];
177
+ for (const dir of exampleDirs) if (existsSync(dir)) await rm(dir, {
178
+ force: true,
179
+ recursive: true
180
+ });
181
+ }
136
182
  };
137
183
 
138
184
  //#endregion
@@ -1,15 +1,15 @@
1
1
  import { Command } from "@oclif/core";
2
- import * as _oclif_core_interfaces6 from "@oclif/core/interfaces";
2
+ import * as _oclif_core_interfaces4 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: _oclif_core_interfaces6.OptionFlag<string | undefined, _oclif_core_interfaces6.CustomOptions>;
10
- tailwind: _oclif_core_interfaces6.BooleanFlag<boolean>;
11
- type: _oclif_core_interfaces6.OptionFlag<string, _oclif_core_interfaces6.CustomOptions>;
12
- verbose: _oclif_core_interfaces6.BooleanFlag<boolean>;
9
+ name: _oclif_core_interfaces4.OptionFlag<string | undefined, _oclif_core_interfaces4.CustomOptions>;
10
+ tailwind: _oclif_core_interfaces4.BooleanFlag<boolean>;
11
+ type: _oclif_core_interfaces4.OptionFlag<string, _oclif_core_interfaces4.CustomOptions>;
12
+ verbose: _oclif_core_interfaces4.BooleanFlag<boolean>;
13
13
  };
14
14
  static id: string;
15
15
  run(): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import { t as BaseCommand } from "../../base-command-BQVnm_gK.js";
2
- import * as _oclif_core_interfaces4 from "@oclif/core/interfaces";
2
+ import * as _oclif_core_interfaces2 from "@oclif/core/interfaces";
3
3
 
4
4
  //#region src/commands/publish/index.d.ts
5
5
  declare class Publish extends BaseCommand<typeof Publish> {
@@ -7,7 +7,7 @@ declare class Publish extends BaseCommand<typeof Publish> {
7
7
  static description: string;
8
8
  static examples: string[];
9
9
  static flags: {
10
- theme: _oclif_core_interfaces4.OptionFlag<string, _oclif_core_interfaces4.CustomOptions>;
10
+ theme: _oclif_core_interfaces2.OptionFlag<string, _oclif_core_interfaces2.CustomOptions>;
11
11
  };
12
12
  getAllFiles(dirPath: string, basePath: string): string[];
13
13
  run(): Promise<void>;
@@ -7,6 +7,7 @@ interface TemplateScope {
7
7
  slug: string;
8
8
  tailwind?: boolean;
9
9
  };
10
+ devtoolsVersion?: string;
10
11
  editorVersion?: string;
11
12
  extensionsVersion?: string;
12
13
  tailwind?: boolean;
package/dist/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { n as StoryGenerator, r as BaseTemplateGenerator, t as JuoTemplateGenerator } from "../juoTemplateGenerator-RkinYN2l.js";
2
- import { t as PackageGenerator } from "../packageGenerator-D81lWXTA.js";
2
+ import { t as PackageGenerator } from "../packageGenerator-CE68rONC.js";
3
3
 
4
4
  export { BaseTemplateGenerator, JuoTemplateGenerator, PackageGenerator, StoryGenerator };
@@ -80,15 +80,16 @@ var PackageGenerator = class {
80
80
  });
81
81
  const frameworkConfig = FRAMEWORK_CONFIGS[options.framework];
82
82
  baseConfig.dependencies = {
83
- "@juo/blocks": "^1.12.0-alpha.2",
84
- "@juo/customer-ui": "^0.5.0-alpha.4",
83
+ "@juo/blocks": "^1.12.0-alpha.3",
84
+ "@juo/customer-ui": "^0.5.0-alpha.6",
85
85
  ...baseConfig.dependencies,
86
86
  ...frameworkConfig.dependencies
87
87
  };
88
88
  baseConfig.devDependencies = {
89
89
  ...baseConfig.devDependencies,
90
90
  ...frameworkConfig.devDependencies,
91
- ...options.storybook ? frameworkConfig.storybookDevDependencies : {}
91
+ ...options.storybook ? frameworkConfig.storybookDevDependencies : {},
92
+ "lit-html": "3.3.0"
92
93
  };
93
94
  return this.sort(baseConfig);
94
95
  }
@@ -179,5 +179,5 @@
179
179
  ]
180
180
  }
181
181
  },
182
- "version": "0.3.0-alpha.1"
182
+ "version": "0.3.0-alpha.3"
183
183
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "juo",
3
3
  "description": "A CLI tool for the Juo platform",
4
- "version": "0.3.0-alpha.1",
4
+ "version": "0.3.0-alpha.3",
5
5
  "author": "Juo (https://juo.io)",
6
6
  "bin": {
7
7
  "juo": "./bin/run.js"
@@ -1,10 +1,8 @@
1
- <link
2
- rel="stylesheet"
3
- href="https://cdn.jsdelivr.net/npm/inter-ui@3.19.3/inter.min.css"
4
- />
1
+ <link rel="preconnect" href="https://rsms.me/" />
2
+ <link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
5
3
  <style>
6
4
  html {
7
- font-family: "Inter var", sans-serif !important;
5
+ font-family: InterVariable, Inter, sans-serif !important;
8
6
  -webkit-font-smoothing: antialiased;
9
7
  -moz-osx-font-smoothing: grayscale;
10
8
  }
@@ -1,7 +1,22 @@
1
1
  import { defineBlock } from "@juo/blocks";
2
2
  import { createPreactRenderer } from "@juo/blocks/preact";
3
- import { {{ block.clsName }} } from "./{{block.clsName}}.tsx";
4
- import type { FromSchema, JSONSchema } from "json-schema-to-ts";
3
+ import type { ExtendedJSONSchema, FromSchema } from "json-schema-to-ts";
4
+ import { {{ block.clsName }} } from "./{{ block.clsName }}.tsx";
5
+
6
+ // Default copy for this block, keyed by translation key. Shipped through the
7
+ // block's `locales` loader so the editor's translation panel can override each
8
+ // string per theme/locale. The keys here are the namespace for this block.
9
+ const enTranslations = {
10
+ title: "{{ block.framework | capitalize }} + TypeScript",
11
+ subtitle: "Starter Block",
12
+ };
13
+
14
+ // Allow the juo-specific schema keywords used below. `format: "color"` is a
15
+ // standard JSON Schema keyword and needs no extension.
16
+ type ExtendedSchema = ExtendedJSONSchema<{
17
+ "x-juo-control-type": string;
18
+ "x-juo-option-labels": Record<string, string>;
19
+ }>;
5
20
 
6
21
  const schema = {
7
22
  type: "object",
@@ -9,31 +24,71 @@ const schema = {
9
24
  props: {
10
25
  type: "object",
11
26
  properties: {
12
- title: { type: "string" },
13
- subtitle: { type: "string" },
14
- initialCount: { type: "number" },
27
+ // `x-juo-control-type: "inline-text"` -> edited directly on the canvas
28
+ // via <juo-text>; the edit is stored as a translation override.
29
+ title: {
30
+ type: "string",
31
+ "x-juo-control-type": "inline-text",
32
+ description: "Heading — edit inline on the canvas.",
33
+ },
34
+ subtitle: {
35
+ type: "string",
36
+ "x-juo-control-type": "inline-text",
37
+ description: "Supporting text — edit inline on the canvas.",
38
+ },
39
+ // `enum` -> Select control. `x-juo-option-labels` gives each option a
40
+ // friendly label in the editor.
41
+ buttonVariant: {
42
+ enum: ["solid", "outline", "ghost"],
43
+ title: "Button style",
44
+ "x-juo-option-labels": {
45
+ solid: "Solid",
46
+ outline: "Outline",
47
+ ghost: "Ghost",
48
+ },
49
+ },
50
+ // `format: "color"` -> Color control
51
+ accentColor: {
52
+ type: "string",
53
+ format: "color",
54
+ title: "Accent color",
55
+ description:
56
+ "Color for the icons. Leave empty to use the default neutral color.",
57
+ },
58
+ // `x-juo-control-type: "stepper"` -> Stepper control
59
+ initialCount: {
60
+ type: "number",
61
+ title: "Initial count",
62
+ "x-juo-control-type": "stepper",
63
+ },
15
64
  },
16
- required: ["title", "subtitle", "initialCount"],
65
+ required: ["title", "subtitle", "buttonVariant", "initialCount"],
17
66
  additionalProperties: false,
18
67
  },
19
68
  },
20
69
  required: ["props"],
21
70
  additionalProperties: false,
22
- } as const satisfies JSONSchema;
71
+ } as const satisfies ExtendedSchema;
23
72
 
24
73
  type Schema = FromSchema<typeof schema>;
25
74
  export type Props = Schema["props"];
26
75
 
27
- export const {{ block.clsName }}Block = defineBlock("{{ block.name }}", {
76
+ export const {{ block.clsName }}Block = defineBlock<Schema>("{{ block.name }}", {
28
77
  group: "theme",
29
78
  schema,
30
79
  initialValue: () => ({
31
80
  props: {
32
- title: "{{ block.framework | capitalize }} + TypeScript",
33
- subtitle: "Starter Block",
81
+ title: enTranslations.title,
82
+ subtitle: enTranslations.subtitle,
83
+ buttonVariant: "solid",
84
+ accentColor: "",
34
85
  initialCount: 0,
35
86
  },
36
87
  slots: {},
37
88
  }),
38
89
  renderer: createPreactRenderer({{ block.clsName }}),
90
+ locales: {
91
+ supported: ["en"],
92
+ load: async () => enTranslations,
93
+ },
39
94
  });