@jxsuite/create 0.19.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/generate.js ADDED
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Shared project generation logic. Used by both the CLI scaffolder and the Studio server endpoint.
3
+ */
4
+
5
+ import { mkdir, writeFile, cp } from "node:fs/promises";
6
+ import { join, dirname } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { existsSync } from "node:fs";
9
+
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+ const TEMPLATE_DIR = join(__dirname, "template");
12
+
13
+ /**
14
+ * @typedef {{
15
+ * name: string;
16
+ * description?: string;
17
+ * url?: string;
18
+ * adapter?: "static" | "cloudflare-pages" | "node" | "bun";
19
+ * }} ProjectOptions
20
+ */
21
+
22
+ /**
23
+ * Generate a new Jx project at the given path.
24
+ *
25
+ * @param {string} destPath — absolute path to the project directory
26
+ * @param {ProjectOptions} opts
27
+ */
28
+ export async function generateProject(destPath, opts) {
29
+ const { name, description = "", url = "", adapter = "static" } = opts;
30
+
31
+ if (existsSync(destPath)) {
32
+ const { readdirSync } = await import("node:fs");
33
+ if (readdirSync(destPath).length > 0) {
34
+ throw new Error(`Directory "${destPath}" is not empty`);
35
+ }
36
+ }
37
+
38
+ await mkdir(destPath, { recursive: true });
39
+ await mkdir(join(destPath, "components"), { recursive: true });
40
+ await mkdir(join(destPath, "public"), { recursive: true });
41
+ await mkdir(join(destPath, "content"), { recursive: true });
42
+
43
+ const projectJson = buildProjectJson({ name, description, url, adapter });
44
+ const packageJson = buildPackageJson({ name, description, adapter });
45
+
46
+ await Promise.all([
47
+ writeFile(join(destPath, "project.json"), JSON.stringify(projectJson, null, "\t") + "\n"),
48
+ writeFile(join(destPath, "package.json"), JSON.stringify(packageJson, null, " ") + "\n"),
49
+ cp(join(TEMPLATE_DIR, "gitignore"), join(destPath, ".gitignore")),
50
+ cp(join(TEMPLATE_DIR, "layouts"), join(destPath, "layouts"), { recursive: true }),
51
+ cp(join(TEMPLATE_DIR, "pages"), join(destPath, "pages"), { recursive: true }),
52
+ ]);
53
+ }
54
+
55
+ /**
56
+ * @param {ProjectOptions} opts
57
+ */
58
+ function buildProjectJson({ name, description, url, adapter }) {
59
+ const $head = [
60
+ {
61
+ tagName: "meta",
62
+ attributes: { name: "viewport", content: "width=device-width, initial-scale=1" },
63
+ },
64
+ ];
65
+
66
+ if (description) {
67
+ $head.push({
68
+ tagName: "meta",
69
+ attributes: { name: "description", content: description },
70
+ });
71
+ }
72
+
73
+ /** @type {{ outDir: string; format: string; trailingSlash: string; adapter?: string }} */
74
+ const build = {
75
+ outDir: "./dist",
76
+ format: "directory",
77
+ trailingSlash: "always",
78
+ };
79
+
80
+ if (adapter && adapter !== "static") {
81
+ build.adapter = adapter;
82
+ }
83
+
84
+ return {
85
+ name,
86
+ url: url || "https://example.com",
87
+ defaults: {
88
+ layout: "./layouts/base.json",
89
+ lang: "en",
90
+ },
91
+ $head,
92
+ $media: {
93
+ "--": "1280px",
94
+ "--lg": "(max-width: 1024px)",
95
+ "--md": "(max-width: 768px)",
96
+ "--sm": "(max-width: 640px)",
97
+ },
98
+ style: {
99
+ margin: "0",
100
+ padding: "0",
101
+ fontFamily: "system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif",
102
+ lineHeight: "1.6",
103
+ color: "#1a1a1a",
104
+ },
105
+ build,
106
+ };
107
+ }
108
+
109
+ /**
110
+ * @param {{ name: string; description?: string; adapter?: string }} opts
111
+ */
112
+ function buildPackageJson({ name, description, adapter }) {
113
+ const slug = name
114
+ .toLowerCase()
115
+ .replace(/[^a-z0-9]+/g, "-")
116
+ .replace(/^-|-$/g, "");
117
+
118
+ /** @type {Record<string, string>} */
119
+ const devDependencies = {
120
+ "@jxsuite/compiler": "^0.19.0",
121
+ "@jxsuite/runtime": "^0.19.0",
122
+ };
123
+
124
+ if (adapter === "cloudflare-pages") {
125
+ devDependencies["wrangler"] = "^4";
126
+ }
127
+
128
+ return {
129
+ name: slug,
130
+ private: true,
131
+ description: description || "",
132
+ license: "MIT",
133
+ scripts: {
134
+ build: "jx build",
135
+ dev: "jx dev",
136
+ },
137
+ devDependencies,
138
+ };
139
+ }
package/index.js ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Scaffold a new Jx project.
4
+ *
5
+ * Usage: bun create @jxsuite my-site
6
+ */
7
+
8
+ import { createInterface } from "node:readline/promises";
9
+ import { stdin, stdout } from "node:process";
10
+ import { resolve, basename } from "node:path";
11
+ import { generateProject } from "./generate.js";
12
+
13
+ const dest = process.argv[2];
14
+ if (!dest) {
15
+ console.error("Usage: bun create @jxsuite <directory>");
16
+ process.exit(1);
17
+ }
18
+
19
+ const destPath = resolve(dest);
20
+ const dirName = basename(destPath);
21
+
22
+ const rl = createInterface({ input: stdin, output: stdout });
23
+
24
+ const name = (await rl.question(`Project name (${dirName}): `)) || dirName;
25
+ const description = await rl.question("Description: ");
26
+ const url = await rl.question("Production URL (https://example.com): ");
27
+
28
+ console.log(`
29
+ Deployment adapter:
30
+ 1) static (default)
31
+ 2) cloudflare-pages
32
+ 3) node
33
+ 4) bun
34
+ `);
35
+ const adapterChoice = await rl.question("Adapter [1]: ");
36
+ rl.close();
37
+
38
+ const adapterMap = { "1": "static", "2": "cloudflare-pages", "3": "node", "4": "bun" };
39
+ const adapter = adapterMap[adapterChoice] || "static";
40
+
41
+ await generateProject(destPath, { name, description, url, adapter });
42
+
43
+ console.log(`
44
+ Project created at ${destPath}
45
+
46
+ Next steps:
47
+ cd ${dest}
48
+ bun install
49
+ bun run dev
50
+ `);
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "bin": {
3
+ "create-jxsuite": "./index.js"
4
+ },
5
+ "description": "Scaffold a new Jx project",
6
+ "exports": {
7
+ ".": "./index.js",
8
+ "./generate": "./generate.js"
9
+ },
10
+ "files": [
11
+ "index.js",
12
+ "generate.js",
13
+ "template/"
14
+ ],
15
+ "license": "MIT",
16
+ "name": "@jxsuite/create",
17
+ "repository": {
18
+ "directory": "packages/create",
19
+ "type": "git",
20
+ "url": "git+https://github.com/jxsuite/jx.git"
21
+ },
22
+ "type": "module",
23
+ "version": "0.19.0"
24
+ }
@@ -0,0 +1,3 @@
1
+ .jx-cache
2
+ dist
3
+ node_modules
@@ -0,0 +1,65 @@
1
+ {
2
+ "tagName": "div",
3
+ "style": {
4
+ "margin": "0",
5
+ "padding": "0",
6
+ "minHeight": "100vh",
7
+ "display": "flex",
8
+ "flexDirection": "column",
9
+ "fontFamily": "system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif",
10
+ "lineHeight": "1.6",
11
+ "color": "#1a1a1a"
12
+ },
13
+ "children": [
14
+ {
15
+ "tagName": "header",
16
+ "style": {
17
+ "padding": "1rem 2rem",
18
+ "borderBottom": "1px solid #e5e5e5"
19
+ },
20
+ "children": [
21
+ {
22
+ "tagName": "a",
23
+ "attributes": { "href": "/" },
24
+ "style": {
25
+ "textDecoration": "none",
26
+ "fontSize": "1.25rem",
27
+ "fontWeight": "700",
28
+ "color": "#1a1a1a"
29
+ },
30
+ "children": ["My Site"]
31
+ }
32
+ ]
33
+ },
34
+ {
35
+ "tagName": "main",
36
+ "style": {
37
+ "flex": "1",
38
+ "maxWidth": "800px",
39
+ "margin": "0 auto",
40
+ "padding": "2rem",
41
+ "width": "100%"
42
+ },
43
+ "children": [
44
+ { "tagName": "slot" }
45
+ ]
46
+ },
47
+ {
48
+ "tagName": "footer",
49
+ "style": {
50
+ "padding": "1.5rem 2rem",
51
+ "borderTop": "1px solid #e5e5e5",
52
+ "textAlign": "center",
53
+ "fontSize": "0.875rem",
54
+ "color": "#666"
55
+ },
56
+ "children": [
57
+ {
58
+ "tagName": "p",
59
+ "style": { "margin": "0" },
60
+ "children": ["Built with Jx"]
61
+ }
62
+ ]
63
+ }
64
+ ]
65
+ }
@@ -0,0 +1,7 @@
1
+ ---
2
+ title: Home
3
+ ---
4
+
5
+ # Welcome
6
+
7
+ Your new Jx site is ready. Edit this page at `pages/index.md`.