@jxsuite/create 1.6.0 → 1.8.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 CHANGED
@@ -39,13 +39,13 @@ Two disjoint id namespaces feed `--template`. A **built-in template id wins**; o
39
39
  | `mobile-first` | Same files, mobile-first `$media` (min-width queries, 375px base) |
40
40
  | `mobile-app` | Mobile-first `$media`, `viewport-fit=cover`, a `theme-color` meta, and a file overlay |
41
41
 
42
- `template/` (singular) is the shared skeleton copied on every non-starter scaffold: `gitignore` → `.gitignore`, `layouts/`, `pages/`. `templates/` (plural) holds per-template overlay trees copied **after** the skeleton with `force: true`, and today contains exactly one: `mobile-app/`, which replaces `layouts/base.json` with an app shell (bottom nav bar, safe-area insets) and `pages/index.md` with an app-flavored home page, and adds `pages/explore.md` and `pages/profile.md`. So `blank`, `desktop-first` and `mobile-first` differ only in the generated `project.json` (zero file differences). Breakpoint maps live in `templates.ts` (`mediaForTemplate`).
42
+ `template/` (singular) is the shared skeleton copied on every non-starter scaffold: `gitignore` → `.gitignore`, `layouts/`, `pages/`. `templates/` (plural) holds per-template overlay trees copied **after** the skeleton with `force: true`, and today contains exactly one: `mobile-app/`, which replaces `layouts/base.jxn` with an app shell (bottom nav bar, safe-area insets) and `pages/index.md` with an app-flavored home page, and adds `pages/explore.md` and `pages/profile.md`. So `blank`, `desktop-first` and `mobile-first` differ only in the generated `project.json` (zero file differences). Breakpoint maps live in `templates.ts` (`mediaForTemplate`).
43
43
 
44
44
  Starter ids come from [`@jxsuite/starters`](../starters/README.md). Its `registry.json` is the current list. A starter clone copies the starter tree verbatim except `node_modules`, `dist`, `.cache`, `.jx-cache`, `.git`, and `images.json`, then re-stamps `project.json` in place: `name` always, `url` only when non-empty, `build.adapter` only when the adapter is not `static`, and the `<meta name="description">` content only when a description was given. The starter's design tokens, content collections, image pipeline, and the rest of `$head` survive; its `package.json` does not, because it is rebuilt from scratch (see below).
45
45
 
46
46
  ## What gets generated
47
47
 
48
- `project.json` leads with `$schema: "./project.schema.json"` and carries `$head`, `$media`, `build` (`{ outDir: "./dist", trailingSlash: "always" }`, plus `adapter` when it is not `static`), `content: {}`, `defaults: { lang: "en", layout: "./layouts/base.json" }`, `extensions: ["@jxsuite/parser"]`, `name`, `style`, and `url`.
48
+ `project.json` leads with `$schema: "./project.schema.json"` and carries `$head`, `$media`, `build` (`{ outDir: "./dist", trailingSlash: "always" }`, plus `adapter` when it is not `static`), `content: {}`, `defaults: { lang: "en", layout: "./layouts/base.jxn" }`, `extensions: ["@jxsuite/parser"]`, `name`, `style`, and `url`.
49
49
 
50
50
  `package.json` is `private: true`, MIT, with `build`/`dev`/`preview` scripts calling the `jx` CLI. Its name is the project name lowercased with non-alphanumerics collapsed to hyphens. `@jxsuite/parser` is a dependency; `@jxsuite/compiler`, `@jxsuite/runtime` and `@jxsuite/server` are devDependencies. Any non-static adapter adds `hono: "^4"`; the two Cloudflare adapters additionally add `wrangler: "^4"`, a `deploy` script (`wrangler deploy` or `wrangler pages deploy dist`), and a generated `wrangler.jsonc`.
51
51
 
package/generate.ts CHANGED
@@ -205,7 +205,10 @@ async function populateProject(destPath: string, opts: ProjectOptions) {
205
205
  const packageJson = buildPackageJson({ adapter, description, name });
206
206
 
207
207
  const writes = [
208
- writeFile(join(destPath, NEW_PROJECT_FILE_NAME), serializeProjectFile(projectJson, null)),
208
+ writeFile(
209
+ join(destPath, NEW_PROJECT_FILE_NAME),
210
+ withExtensionsNote(serializeProjectFile(projectJson, null)),
211
+ ),
209
212
  writeFile(join(destPath, "package.json"), `${JSON.stringify(packageJson, null, " ")}\n`),
210
213
  cp(join(TEMPLATE_DIR, "gitignore"), join(destPath, ".gitignore")),
211
214
  cp(join(TEMPLATE_DIR, "layouts"), join(destPath, "layouts"), { recursive: true }),
@@ -218,6 +221,7 @@ async function populateProject(destPath: string, opts: ProjectOptions) {
218
221
  }
219
222
 
220
223
  await Promise.all(writes);
224
+ await writeEditorSupport(destPath);
221
225
 
222
226
  // The .gitignore, layouts/ and pages/ above are copies too, carrying the template's modes.
223
227
  // The overlay below would otherwise fail to `force` over a file that came across read-only.
@@ -298,6 +302,9 @@ async function scaffoldFromStarter(
298
302
  applyDesign(project, design, { starter: true });
299
303
  }
300
304
  await writeFile(projectPath, serializeProjectFile(project, starterText, projectPath));
305
+ await writeEditorSupport(destPath);
306
+ // Copied from a read-only install as the rest was, so made writable as the rest was.
307
+ await makeWritable(destPath);
301
308
 
302
309
  // Rebuild package.json so scaffolded projects get current dep ranges and scripts regardless of
303
310
  // What the in-repo starter pinned.
@@ -314,6 +321,36 @@ async function scaffoldFromStarter(
314
321
  }
315
322
  }
316
323
 
324
+ /**
325
+ * A new project file, with one comment in it saying what `extensions` is for: the line a new user
326
+ * most needs explained, and the format's main advantage shown rather than described
327
+ * (`site-architecture.md` §2.4). The canonical form has no comments, so it goes in after.
328
+ *
329
+ * @param text The project file in its canonical form.
330
+ */
331
+ function withExtensionsNote(text: string): string {
332
+ return text.replace(
333
+ /^extensions\b/m,
334
+ "// Extensions add what Jx does not do on its own. @jxsuite/parser reads pages/index.md and\n" +
335
+ "// every Markdown collection; turn others on in Studio, under Project Settings > Extensions.\n" +
336
+ "extensions",
337
+ );
338
+ }
339
+
340
+ /**
341
+ * What lets an editor and GitHub read `.jxn` from the first commit: Linguist knows KDL only as
342
+ * `.kdl`, and VS Code needs telling too. Both cost nothing, and an existing file is kept.
343
+ *
344
+ * @param destPath The project's root.
345
+ */
346
+ async function writeEditorSupport(destPath: string): Promise<void> {
347
+ await mkdir(join(destPath, ".vscode"), { recursive: true });
348
+ await Promise.all([
349
+ cp(join(TEMPLATE_DIR, "gitattributes"), join(destPath, ".gitattributes"), { force: false }),
350
+ cp(join(TEMPLATE_DIR, "vscode"), join(destPath, ".vscode"), { force: false, recursive: true }),
351
+ ]);
352
+ }
353
+
317
354
  /**
318
355
  * Apply the design quickstart (colors, fonts, breakpoints) to a project.json object, in place.
319
356
  *
@@ -499,7 +536,7 @@ function buildProjectJson({ name, description, url, adapter, template = "blank"
499
536
  content: {},
500
537
  defaults: {
501
538
  lang: "en",
502
- layout: "./layouts/base.json",
539
+ layout: "./layouts/base.jxn",
503
540
  },
504
541
  extensions: ["@jxsuite/parser"],
505
542
  name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/create",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "Scaffold a new Jx project",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -37,7 +37,7 @@
37
37
  "test:coverage": "bun test --isolate --coverage"
38
38
  },
39
39
  "dependencies": {
40
- "@jxsuite/schema": "^5.4.0",
41
- "@jxsuite/starters": "^3.3.2"
40
+ "@jxsuite/schema": "^5.6.0",
41
+ "@jxsuite/starters": "^3.5.0"
42
42
  }
43
43
  }
@@ -0,0 +1,2 @@
1
+ # Jx Notation is KDL 2, which GitHub highlights only as .kdl
2
+ *.jxn linguist-language=KDL
@@ -0,0 +1,38 @@
1
+ /- kdl-version 2
2
+
3
+ div {
4
+ style {
5
+ margin "0"
6
+ padding "0"
7
+ minHeight "100vh"
8
+ display flex
9
+ flexDirection column
10
+ fontFamily "system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif"
11
+ lineHeight "1.6"
12
+ color "#1a1a1a"
13
+ }
14
+ header {
15
+ style padding="1rem 2rem" borderBottom="1px solid #e5e5e5"
16
+ a href="/" {
17
+ style textDecoration=none fontSize="1.25rem" fontWeight="700" color="#1a1a1a"
18
+ - "My Site"
19
+ }
20
+ }
21
+ main {
22
+ style flex="1" maxWidth="800px" margin="0 auto" padding="2rem" width="100%"
23
+ slot
24
+ }
25
+ footer {
26
+ style {
27
+ padding "1.5rem 2rem"
28
+ borderTop "1px solid #e5e5e5"
29
+ textAlign center
30
+ fontSize "0.875rem"
31
+ color "#666"
32
+ }
33
+ p {
34
+ style margin="0"
35
+ - "Built with Jx"
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "recommendations": ["kdl-org.kdl"]
3
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "files.associations": {
3
+ "*.jxn": "kdl"
4
+ }
5
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
- "parser": "^4.3.2",
3
- "compiler": "^8.4.0",
4
- "runtime": "^6.2.1",
5
- "server": "^8.5.0"
2
+ "parser": "^4.3.4",
3
+ "compiler": "^8.6.0",
4
+ "runtime": "^6.3.0",
5
+ "server": "^8.7.0"
6
6
  }
@@ -0,0 +1,73 @@
1
+ /- kdl-version 2
2
+
3
+ div {
4
+ style {
5
+ margin "0"
6
+ padding "0"
7
+ height "100dvh"
8
+ display flex
9
+ flexDirection column
10
+ fontFamily "system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif"
11
+ lineHeight "1.6"
12
+ color "#1a1a1a"
13
+ backgroundColor "#ffffff"
14
+ }
15
+ header {
16
+ style {
17
+ padding "0.75rem 1rem"
18
+ paddingTop "calc(0.75rem + env(safe-area-inset-top))"
19
+ borderBottom "1px solid #e5e5e5"
20
+ textAlign center
21
+ }
22
+ span {
23
+ style fontSize="1.05rem" fontWeight="700"
24
+ - "My App"
25
+ }
26
+ }
27
+ main {
28
+ style flex="1" overflowY=auto padding="1rem" width="100%"
29
+ slot
30
+ }
31
+ nav {
32
+ style {
33
+ display flex
34
+ borderTop "1px solid #e5e5e5"
35
+ paddingBottom "env(safe-area-inset-bottom)"
36
+ backgroundColor "#ffffff"
37
+ }
38
+ a href="/" {
39
+ style {
40
+ flex "1"
41
+ padding "0.75rem 0"
42
+ textAlign center
43
+ textDecoration none
44
+ fontSize "0.8rem"
45
+ fontWeight "600"
46
+ color "#1a1a1a"
47
+ }
48
+ - Home
49
+ }
50
+ a href="/explore/" {
51
+ style {
52
+ flex "1"
53
+ padding "0.75rem 0"
54
+ textAlign center
55
+ textDecoration none
56
+ fontSize "0.8rem"
57
+ color "#666"
58
+ }
59
+ - Explore
60
+ }
61
+ a href="/profile/" {
62
+ style {
63
+ flex "1"
64
+ padding "0.75rem 0"
65
+ textAlign center
66
+ textDecoration none
67
+ fontSize "0.8rem"
68
+ color "#666"
69
+ }
70
+ - Profile
71
+ }
72
+ }
73
+ }
@@ -4,4 +4,4 @@ title: Home
4
4
 
5
5
  # Welcome
6
6
 
7
- Your new Jx app is ready. Edit this page at `pages/index.md`, and the app shell (header and bottom navigation) at `layouts/base.json`.
7
+ Your new Jx app is ready. Edit this page at `pages/index.md`, and the app shell (header and bottom navigation) at `layouts/base.jxn`.
@@ -1,63 +0,0 @@
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": [{ "tagName": "slot" }]
44
- },
45
- {
46
- "tagName": "footer",
47
- "style": {
48
- "padding": "1.5rem 2rem",
49
- "borderTop": "1px solid #e5e5e5",
50
- "textAlign": "center",
51
- "fontSize": "0.875rem",
52
- "color": "#666"
53
- },
54
- "children": [
55
- {
56
- "tagName": "p",
57
- "style": { "margin": "0" },
58
- "children": ["Built with Jx"]
59
- }
60
- ]
61
- }
62
- ]
63
- }
@@ -1,96 +0,0 @@
1
- {
2
- "tagName": "div",
3
- "style": {
4
- "margin": "0",
5
- "padding": "0",
6
- "height": "100dvh",
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
- "backgroundColor": "#ffffff"
13
- },
14
- "children": [
15
- {
16
- "tagName": "header",
17
- "style": {
18
- "padding": "0.75rem 1rem",
19
- "paddingTop": "calc(0.75rem + env(safe-area-inset-top))",
20
- "borderBottom": "1px solid #e5e5e5",
21
- "textAlign": "center"
22
- },
23
- "children": [
24
- {
25
- "tagName": "span",
26
- "style": {
27
- "fontSize": "1.05rem",
28
- "fontWeight": "700"
29
- },
30
- "children": ["My App"]
31
- }
32
- ]
33
- },
34
- {
35
- "tagName": "main",
36
- "style": {
37
- "flex": "1",
38
- "overflowY": "auto",
39
- "padding": "1rem",
40
- "width": "100%"
41
- },
42
- "children": [{ "tagName": "slot" }]
43
- },
44
- {
45
- "tagName": "nav",
46
- "style": {
47
- "display": "flex",
48
- "borderTop": "1px solid #e5e5e5",
49
- "paddingBottom": "env(safe-area-inset-bottom)",
50
- "backgroundColor": "#ffffff"
51
- },
52
- "children": [
53
- {
54
- "tagName": "a",
55
- "attributes": { "href": "/" },
56
- "style": {
57
- "flex": "1",
58
- "padding": "0.75rem 0",
59
- "textAlign": "center",
60
- "textDecoration": "none",
61
- "fontSize": "0.8rem",
62
- "fontWeight": "600",
63
- "color": "#1a1a1a"
64
- },
65
- "children": ["Home"]
66
- },
67
- {
68
- "tagName": "a",
69
- "attributes": { "href": "/explore/" },
70
- "style": {
71
- "flex": "1",
72
- "padding": "0.75rem 0",
73
- "textAlign": "center",
74
- "textDecoration": "none",
75
- "fontSize": "0.8rem",
76
- "color": "#666"
77
- },
78
- "children": ["Explore"]
79
- },
80
- {
81
- "tagName": "a",
82
- "attributes": { "href": "/profile/" },
83
- "style": {
84
- "flex": "1",
85
- "padding": "0.75rem 0",
86
- "textAlign": "center",
87
- "textDecoration": "none",
88
- "fontSize": "0.8rem",
89
- "color": "#666"
90
- },
91
- "children": ["Profile"]
92
- }
93
- ]
94
- }
95
- ]
96
- }