json-to-spec 19.1.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 +118 -0
- package/docs/demo.html +426 -0
- package/docs/dist/min.js +164 -0
- package/docs/dist/v1/min.js +124 -0
- package/docs/dist/v17/min.js +355 -0
- package/docs/dist/v18/min.js +160 -0
- package/docs/dist/v19/min.js +164 -0
- package/docs/dist/v2/min.js +129 -0
- package/docs/dist/v3/min.js +201 -0
- package/docs/index.html +248 -0
- package/docs/pages/architecture.html +115 -0
- package/docs/pages/how-it-works.html +125 -0
- package/docs/pages/what-not.html +100 -0
- package/docs/pages/what.html +105 -0
- package/docs/pages/why.html +88 -0
- package/docs/samples/form.html +265 -0
- package/docs/samples/index.html +112 -0
- package/docs/samples/table.html +306 -0
- package/index.js +2 -0
- package/package.json +50 -0
- package/samples/dataList/index.html +31 -0
- package/samples/dataList/index.js +62 -0
- package/samples/dataList/input/data.json +5043 -0
- package/samples/dataList/input/structure.json +38 -0
- package/samples/dataList copy/index.html +30 -0
- package/samples/dataList copy/index.js +61 -0
- package/samples/dataList copy/input/data.json +35307 -0
- package/samples/dataList copy/input/structure.json +18 -0
- package/samples/form/index.html +29 -0
- package/samples/form/index.js +64 -0
- package/samples/form/input/data.json +9 -0
- package/samples/form/input/structure.json +192 -0
- package/samples/form-/data.json +42 -0
- package/samples/form-/structure.json +110 -0
- package/samples/select/index.html +31 -0
- package/samples/select/index.js +61 -0
- package/samples/select/input/data.json +35287 -0
- package/samples/select/input/select.json +18 -0
- package/samples/select/input/structure.json +47 -0
- package/samples/table/index.html +29 -0
- package/samples/table/index.js +61 -0
- package/samples/table/input/data.json +35287 -0
- package/samples/table/input/structure copy.json +47 -0
- package/samples/table/input/structure.json +86 -0
- package/samples/table/input/table.json +135 -0
- package/samples/table-/data.json +68 -0
- package/samples/table-/structure.json +235 -0
- package/src/index.js +2 -0
- package/src/v10/index.js +96 -0
- package/src/v10/instructionEngine/compile/compileTree.js +191 -0
- package/src/v10/instructionEngine/compileTree.js +1 -0
- package/src/v10/instructionEngine/expandIteration.js +1 -0
- package/src/v10/instructionEngine/index.js +29 -0
- package/src/v10/instructionEngine/interpolate.js +2 -0
- package/src/v10/instructionEngine/iteration/expandIteration.js +95 -0
- package/src/v10/instructionEngine/operation/operation.js +237 -0
- package/src/v10/instructionEngine/other/other.js +150 -0
- package/src/v10/instructionEngine/pipeline/pipeline.js +47 -0
- package/src/v10/instructionEngine/replace/replace.js +173 -0
- package/src/v10/instructionEngine/resolve/resolvePath.js +18 -0
- package/src/v10/instructionEngine/resolve/resolveTokenValue.js +22 -0
- package/src/v10/instructionEngine/resolvePath.js +1 -0
- package/src/v10/instructionEngine/resolveTokenValue.js +1 -0
- package/src/v10/instructionEngine/story/storyInjector.js +50 -0
- package/src/v10/instructionEngine/storyInjector.js +1 -0
- package/src/v10/instructionEngine/value/interpolate.js +38 -0
- package/src/v11/index.js +116 -0
- package/src/v11/iterate.js +270 -0
- package/src/v11/replace.js +169 -0
- package/src/v11/resolvePath.js +29 -0
- package/src/v12/index.js +122 -0
- package/src/v12/iterate/bindFormValues.js +74 -0
- package/src/v12/iterate/expandIteration.js +181 -0
- package/src/v12/iterate/index.js +10 -0
- package/src/v12/iterate/iterate.js +56 -0
- package/src/v12/iterate/iterateNode.js +86 -0
- package/src/v12/iterate/resolveCollection.js +37 -0
- package/src/v12/iterate.js +270 -0
- package/src/v12/replace.js +169 -0
- package/src/v12/resolvePath.js +29 -0
- package/src/v13/index.js +110 -0
- package/src/v13/iterate/bindFormValues.js +74 -0
- package/src/v13/iterate/expandIteration.js +188 -0
- package/src/v13/iterate/index.js +9 -0
- package/src/v13/iterate/iterate.js +32 -0
- package/src/v13/iterate/iterateNode.js +86 -0
- package/src/v13/iterate/resolveCollection.js +37 -0
- package/src/v13/iterate/walk.js +90 -0
- package/src/v13/iterate.js +270 -0
- package/src/v13/replace.js +169 -0
- package/src/v13/resolvePath.js +29 -0
- package/src/v14/index.js +112 -0
- package/src/v14/iterate/bindFormValues.js +74 -0
- package/src/v14/iterate/expandIteration.js +188 -0
- package/src/v14/iterate/index.js +9 -0
- package/src/v14/iterate/iterate.js +32 -0
- package/src/v14/iterate/resolveCollection.js +37 -0
- package/src/v14/iterate/walk/walk copy 2.js +114 -0
- package/src/v14/iterate/walk/walk copy.js +90 -0
- package/src/v14/iterate/walk/walk.js +98 -0
- package/src/v14/iterate.js +270 -0
- package/src/v14/replace/index.js +16 -0
- package/src/v14/replace/walk.js +90 -0
- package/src/v14/replace.js +153 -0
- package/src/v14/resolvePath.js +29 -0
- package/src/v15/index.js +123 -0
- package/src/v15/iterate/bindFormValues.js +74 -0
- package/src/v15/iterate/expandIteration.js +188 -0
- package/src/v15/iterate/index.js +9 -0
- package/src/v15/iterate/iterate.js +32 -0
- package/src/v15/iterate/resolveCollection.js +37 -0
- package/src/v15/iterate/walk/walk copy 2.js +114 -0
- package/src/v15/iterate/walk/walk copy.js +90 -0
- package/src/v15/iterate/walk/walk.js +195 -0
- package/src/v15/iterate.js +270 -0
- package/src/v15/replace/index.js +16 -0
- package/src/v15/replace/walk.js +90 -0
- package/src/v15/replace.js +155 -0
- package/src/v15/resolvePath.js +29 -0
- package/src/v16/index.js +129 -0
- package/src/v16/iterate/bindFormValues.js +74 -0
- package/src/v16/iterate/expandIteration.js +188 -0
- package/src/v16/iterate/index.js +9 -0
- package/src/v16/iterate/iterate.js +32 -0
- package/src/v16/iterate/resolveCollection.js +37 -0
- package/src/v16/iterate/walk/walk copy 2.js +114 -0
- package/src/v16/iterate/walk/walk copy.js +90 -0
- package/src/v16/iterate/walk/walk.js +196 -0
- package/src/v16/iterate.js +270 -0
- package/src/v16/replace/index.js +16 -0
- package/src/v16/replace/walk.js +90 -0
- package/src/v16/replace.js +155 -0
- package/src/v16/resolvePath.js +29 -0
- package/src/v17/index.js +119 -0
- package/src/v17/iterate/bindFormValues.js +74 -0
- package/src/v17/iterate/expandIteration.js +188 -0
- package/src/v17/iterate/index.js +9 -0
- package/src/v17/iterate/iterate.js +32 -0
- package/src/v17/iterate/resolveCollection.js +37 -0
- package/src/v17/iterate/walk/iterateDo.js +42 -0
- package/src/v17/iterate/walk/replaceWithData.js +60 -0
- package/src/v17/iterate/walk/walk.js +94 -0
- package/src/v17/iterate.js +270 -0
- package/src/v17/replace/index.js +16 -0
- package/src/v17/replace/walk.js +90 -0
- package/src/v17/replace.js +160 -0
- package/src/v17/resolvePath.js +29 -0
- package/src/v18/index.js +66 -0
- package/src/v18/meta.js +7 -0
- package/src/v18/registerGlobal.js +17 -0
- package/src/v18/replace.js +18 -0
- package/src/v18/resolvePath.js +29 -0
- package/src/v18/walk/iterate/v1/index.js +42 -0
- package/src/v18/walk/iterate/v2/index.js +32 -0
- package/src/v18/walk/iterate/v3/index.js +46 -0
- package/src/v18/walk/iterate/v4/createChildren.js +24 -0
- package/src/v18/walk/iterate/v4/index.js +25 -0
- package/src/v18/walk/replaceWithData/v1/index.js +42 -0
- package/src/v18/walk/replaceWithData/v1/replaceCommon.js +21 -0
- package/src/v18/walk/replaceWithData/v2/index.js +105 -0
- package/src/v18/walk/replaceWithData/v2/replaceCommon.js +21 -0
- package/src/v18/walk/replaceWithData/v3/index.js +15 -0
- package/src/v18/walk/replaceWithData/v3/replaceAttributes.js +29 -0
- package/src/v18/walk/replaceWithData/v3/replaceCommon.js +23 -0
- package/src/v18/walk/replaceWithData/v3/replaceNodeValue.js +27 -0
- package/src/v18/walk/walk.js +94 -0
- package/src/v19/index.js +55 -0
- package/src/v19/meta.js +7 -0
- package/src/v19/registerGlobal.js +17 -0
- package/src/v19/replace.js +18 -0
- package/src/v19/resolvePath.js +29 -0
- package/src/v19/walk/iterate/v1/index.js +42 -0
- package/src/v19/walk/iterate/v2/index.js +32 -0
- package/src/v19/walk/iterate/v3/index.js +46 -0
- package/src/v19/walk/iterate/v4/createChildren.js +27 -0
- package/src/v19/walk/iterate/v4/index.js +25 -0
- package/src/v19/walk/replaceWithData/v1/index.js +42 -0
- package/src/v19/walk/replaceWithData/v1/replaceCommon.js +21 -0
- package/src/v19/walk/replaceWithData/v2/index.js +105 -0
- package/src/v19/walk/replaceWithData/v2/replaceCommon.js +21 -0
- package/src/v19/walk/replaceWithData/v3/index.js +15 -0
- package/src/v19/walk/replaceWithData/v3/replaceAttributes.js +29 -0
- package/src/v19/walk/replaceWithData/v3/replaceCommon.js +23 -0
- package/src/v19/walk/replaceWithData/v3/replaceNodeValue.js +27 -0
- package/src/v19/walk/walk.js +97 -0
- package/src/v8/index.js +69 -0
- package/src/v8/instructionEngine/compile/compileTree.js +191 -0
- package/src/v8/instructionEngine/compileTree.js +1 -0
- package/src/v8/instructionEngine/expandIteration.js +1 -0
- package/src/v8/instructionEngine/index.js +16 -0
- package/src/v8/instructionEngine/interpolate.js +2 -0
- package/src/v8/instructionEngine/iteration/expandIteration.js +95 -0
- package/src/v8/instructionEngine/resolve/resolvePath.js +18 -0
- package/src/v8/instructionEngine/resolve/resolveTokenValue.js +22 -0
- package/src/v8/instructionEngine/resolvePath.js +1 -0
- package/src/v8/instructionEngine/resolveTokenValue.js +1 -0
- package/src/v8/instructionEngine/story/storyInjector.js +50 -0
- package/src/v8/instructionEngine/storyInjector.js +1 -0
- package/src/v8/instructionEngine/value/interpolate.js +38 -0
- package/src/v9/index.js +96 -0
- package/src/v9/instructionEngine/compile/compileTree.js +191 -0
- package/src/v9/instructionEngine/compileTree.js +1 -0
- package/src/v9/instructionEngine/expandIteration.js +1 -0
- package/src/v9/instructionEngine/index.js +29 -0
- package/src/v9/instructionEngine/interpolate.js +2 -0
- package/src/v9/instructionEngine/iteration/expandIteration.js +95 -0
- package/src/v9/instructionEngine/operation/operation.js +237 -0
- package/src/v9/instructionEngine/other/other.js +150 -0
- package/src/v9/instructionEngine/pipeline/pipeline.js +47 -0
- package/src/v9/instructionEngine/replace/replace.js +173 -0
- package/src/v9/instructionEngine/resolve/resolvePath.js +18 -0
- package/src/v9/instructionEngine/resolve/resolveTokenValue.js +22 -0
- package/src/v9/instructionEngine/resolvePath.js +1 -0
- package/src/v9/instructionEngine/resolveTokenValue.js +1 -0
- package/src/v9/instructionEngine/story/storyInjector.js +50 -0
- package/src/v9/instructionEngine/storyInjector.js +1 -0
- package/src/v9/instructionEngine/value/interpolate.js +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# json-to-spec
|
|
2
|
+
|
|
3
|
+
A story about taking two JSON inputs and turning them into a final, concrete UI spec.
|
|
4
|
+
|
|
5
|
+
At the center of this project is a simple idea:
|
|
6
|
+
|
|
7
|
+
- `structure` is the blueprint: the shape, layout, rules, and instructions
|
|
8
|
+
- `data` is the truth: fields, rows, values, arrays, and metadata
|
|
9
|
+
- `compile` is the story engine: it resolves the values, replaces placeholders, and expands iterations
|
|
10
|
+
|
|
11
|
+
The final output is a clean, serializable JSON spec, ready for rendering by `json-to-dom` or any similar consumer.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## The story in one line
|
|
16
|
+
|
|
17
|
+
`json-to-spec` is a compiler for declarative UI: it reads a blueprint and a data payload, resolves what belongs where, injects values, expands loops, and emits a final specification tree.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## The three core operations
|
|
22
|
+
|
|
23
|
+
This repo is best understood as a pipeline with three essential stages:
|
|
24
|
+
|
|
25
|
+
1. `resolve` — find the value from the active context or root data
|
|
26
|
+
2. `replace` — replace `${...}` tokens inside strings, attributes, and text content
|
|
27
|
+
3. `iterate` — expand collection-driven operations into concrete child nodes
|
|
28
|
+
|
|
29
|
+
That is the heart of the architecture. Everything else is supporting structure around this idea.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Why this design matters
|
|
34
|
+
|
|
35
|
+
Most UI systems treat layout and data as separate problems, but they still get tangled in components, templates, and imperative logic.
|
|
36
|
+
|
|
37
|
+
`json-to-spec` keeps them decoupled:
|
|
38
|
+
|
|
39
|
+
- the structure describes how a screen should look
|
|
40
|
+
- the data tells what should appear
|
|
41
|
+
- the compiler merges them deterministically
|
|
42
|
+
|
|
43
|
+
This makes the flow predictable, testable, and easy to reason about.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 📖 Start Here
|
|
48
|
+
|
|
49
|
+
- **What is json-to-spec?**: [docs/pages/what.html](docs/pages/what.html)
|
|
50
|
+
- **Why this project exists**: [docs/pages/why.html](docs/pages/why.html)
|
|
51
|
+
- **How the compiler works**: [docs/pages/how-it-works.html](docs/pages/how-it-works.html)
|
|
52
|
+
- **Runtime architecture**: [docs/pages/architecture.html](docs/pages/architecture.html)
|
|
53
|
+
- **Documentation hub**: [docs/index.html](docs/index.html)
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## ⚡ Quick Start
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install
|
|
61
|
+
npm run dev
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 💻 Minimal Example
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
import { compile } from "json-to-spec";
|
|
70
|
+
|
|
71
|
+
const structure = {
|
|
72
|
+
tagName: "div",
|
|
73
|
+
attributes: { class: "card" },
|
|
74
|
+
children: [
|
|
75
|
+
{ tagName: "h3", textContent: "${title}" },
|
|
76
|
+
{
|
|
77
|
+
operation: "iterate",
|
|
78
|
+
source: "users",
|
|
79
|
+
template: {
|
|
80
|
+
tagName: "div",
|
|
81
|
+
textContent: "${$number}. ${name} (${role})"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const data = {
|
|
88
|
+
title: "Engineering Team",
|
|
89
|
+
users: [
|
|
90
|
+
{ name: "Karthik", role: "Architect" },
|
|
91
|
+
{ name: "Praveen", role: "Engineer" }
|
|
92
|
+
]
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const spec = compile({ inStructure: structure, inData: data });
|
|
96
|
+
console.log(spec);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The compile flow is:
|
|
100
|
+
|
|
101
|
+
- resolve the values inside `${...}`
|
|
102
|
+
- replace tokens in the structure
|
|
103
|
+
- iterate over `users` and expand the template
|
|
104
|
+
- return the final spec JSON
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 🎯 Scope
|
|
109
|
+
|
|
110
|
+
This project is intentionally focused. It is a compiler, not a renderer, framework, or business-logic engine.
|
|
111
|
+
|
|
112
|
+
It does one job well: it translates declarative structure + data into a final spec tree.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 📄 License
|
|
117
|
+
|
|
118
|
+
MIT © [KeshavSoft](https://github.com/keshavsoft)
|
package/docs/demo.html
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>json-to-spec | Live Interactive Playground</title>
|
|
8
|
+
<meta name="description" content="Test and compile specifications live in the browser using json-to-spec.">
|
|
9
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
10
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
11
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
12
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
13
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
14
|
+
<style>
|
|
15
|
+
body { font-family: 'Inter', sans-serif; }
|
|
16
|
+
code, pre, textarea { font-family: 'JetBrains Mono', monospace; }
|
|
17
|
+
</style>
|
|
18
|
+
</head>
|
|
19
|
+
|
|
20
|
+
<body class="bg-slate-50 text-slate-800 min-h-screen flex flex-col">
|
|
21
|
+
|
|
22
|
+
<!-- Navbar -->
|
|
23
|
+
<header class="sticky top-0 z-50 bg-white/90 backdrop-blur border-b border-slate-200">
|
|
24
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
|
|
25
|
+
<div class="flex items-center space-x-3">
|
|
26
|
+
<a href="index.html" class="flex items-center space-x-2 font-black text-lg text-slate-900 hover:text-indigo-600 transition">
|
|
27
|
+
<span class="w-8 h-8 rounded-lg bg-indigo-600 text-white flex items-center justify-center font-black text-sm">JS</span>
|
|
28
|
+
<span>json-to-spec</span>
|
|
29
|
+
</a>
|
|
30
|
+
<span class="text-xs bg-indigo-50 text-indigo-700 font-semibold px-2 py-0.5 rounded-full border border-indigo-200">Playground</span>
|
|
31
|
+
</div>
|
|
32
|
+
<nav class="flex items-center space-x-4 text-sm font-medium">
|
|
33
|
+
<a href="samples/form.html" class="text-slate-600 hover:text-indigo-600 transition">Form Sample</a>
|
|
34
|
+
<a href="samples/table.html" class="text-slate-600 hover:text-indigo-600 transition">Table Sample</a>
|
|
35
|
+
<a href="samples/index.html" class="text-slate-600 hover:text-indigo-600 transition">Samples Hub</a>
|
|
36
|
+
<a href="index.html" class="text-indigo-600 font-semibold">← Back to Docs</a>
|
|
37
|
+
</nav>
|
|
38
|
+
</div>
|
|
39
|
+
</header>
|
|
40
|
+
|
|
41
|
+
<!-- Main Playground Workspace -->
|
|
42
|
+
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 flex-1 w-full flex flex-col space-y-4">
|
|
43
|
+
|
|
44
|
+
<!-- Controls Bar -->
|
|
45
|
+
<div class="flex flex-wrap items-center justify-between gap-4 bg-white p-4 rounded-xl border border-slate-200 shadow-sm">
|
|
46
|
+
<div class="flex items-center gap-2">
|
|
47
|
+
<span class="text-xs font-bold text-slate-500 uppercase tracking-wide mr-2">Presets:</span>
|
|
48
|
+
<button id="preset-form" class="px-3 py-1.5 bg-indigo-50 hover:bg-indigo-100 text-indigo-700 rounded-lg text-xs font-semibold transition border border-indigo-200">
|
|
49
|
+
Form Engine
|
|
50
|
+
</button>
|
|
51
|
+
<button id="preset-table" class="px-3 py-1.5 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-lg text-xs font-semibold transition border border-slate-200">
|
|
52
|
+
Table Engine
|
|
53
|
+
</button>
|
|
54
|
+
<button id="preset-simple" class="px-3 py-1.5 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-lg text-xs font-semibold transition border border-slate-200">
|
|
55
|
+
Simple Card
|
|
56
|
+
</button>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div class="flex items-center gap-3">
|
|
60
|
+
<span id="compile-status" class="text-xs font-mono text-emerald-600 font-semibold flex items-center gap-1.5">
|
|
61
|
+
<span class="w-2 h-2 rounded-full bg-emerald-500"></span> Ready
|
|
62
|
+
</span>
|
|
63
|
+
<button id="compile-btn" class="px-4 py-1.5 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg text-xs font-bold flex items-center gap-1.5 shadow-sm transition">
|
|
64
|
+
<span>⚡ Compile Spec & Render</span>
|
|
65
|
+
</button>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- 2x2 Grid -->
|
|
70
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4 flex-1">
|
|
71
|
+
|
|
72
|
+
<!-- Left Top: structure.json -->
|
|
73
|
+
<div class="bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden flex flex-col h-[380px]">
|
|
74
|
+
<div class="px-4 py-2.5 bg-slate-50 border-b border-slate-200 flex items-center justify-between">
|
|
75
|
+
<span class="text-xs font-bold text-slate-700 flex items-center gap-2">
|
|
76
|
+
<span class="w-2 h-2 rounded-full bg-purple-500"></span> 1. structure.json (HOW: UI Blueprint)
|
|
77
|
+
</span>
|
|
78
|
+
<span class="text-[10px] font-mono text-slate-400">Editable Template</span>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="p-2 flex-1 flex flex-col bg-slate-900">
|
|
81
|
+
<textarea id="structure-editor" class="w-full flex-1 bg-transparent text-purple-300 p-2 text-xs leading-relaxed focus:outline-none resize-none" spellcheck="false"></textarea>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<!-- Right Top: data.json -->
|
|
86
|
+
<div class="bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden flex flex-col h-[380px]">
|
|
87
|
+
<div class="px-4 py-2.5 bg-slate-50 border-b border-slate-200 flex items-center justify-between">
|
|
88
|
+
<span class="text-xs font-bold text-slate-700 flex items-center gap-2">
|
|
89
|
+
<span class="w-2 h-2 rounded-full bg-emerald-500"></span> 2. data.json (WHAT: Collections & Values)
|
|
90
|
+
</span>
|
|
91
|
+
<span class="text-[10px] font-mono text-slate-400">Editable Payload</span>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="p-2 flex-1 flex flex-col bg-slate-900">
|
|
94
|
+
<textarea id="data-editor" class="w-full flex-1 bg-transparent text-emerald-300 p-2 text-xs leading-relaxed focus:outline-none resize-none" spellcheck="false"></textarea>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<!-- Left Bottom: Compiled Spec JSON Output -->
|
|
99
|
+
<div class="bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden flex flex-col h-[420px]">
|
|
100
|
+
<div class="px-4 py-2.5 bg-slate-50 border-b border-slate-200 flex items-center justify-between">
|
|
101
|
+
<span class="text-xs font-bold text-slate-700 flex items-center gap-2">
|
|
102
|
+
<span class="w-2 h-2 rounded-full bg-amber-500"></span> 3. Compiled Spec JSON (json-to-spec Output)
|
|
103
|
+
</span>
|
|
104
|
+
<button id="copy-spec-btn" class="px-2 py-0.5 text-[11px] bg-slate-100 hover:bg-slate-200 rounded font-mono text-slate-600 border border-slate-200 transition">
|
|
105
|
+
Copy Spec
|
|
106
|
+
</button>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="p-2 flex-1 flex flex-col bg-slate-900 overflow-auto">
|
|
109
|
+
<pre id="spec-output" class="text-amber-300 text-xs p-2 leading-relaxed"></pre>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<!-- Right Bottom: Native Browser DOM Output -->
|
|
114
|
+
<div class="bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden flex flex-col h-[420px]">
|
|
115
|
+
<div class="px-4 py-2.5 bg-slate-50 border-b border-slate-200 flex items-center justify-between">
|
|
116
|
+
<span class="text-xs font-bold text-slate-700 flex items-center gap-2">
|
|
117
|
+
<span class="w-2 h-2 rounded-full bg-emerald-500 animate-pulse"></span> 4. Live Rendered DOM (via json-to-dom Engine)
|
|
118
|
+
</span>
|
|
119
|
+
<span class="px-2 py-0.5 bg-emerald-50 text-emerald-700 rounded font-mono text-xs border border-emerald-200">Live DOM Node</span>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="p-6 flex-1 overflow-auto bg-slate-50" id="output-wrapper">
|
|
122
|
+
<div id="app" class="w-full">
|
|
123
|
+
<!-- Rendered DOM inserted here -->
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
</div>
|
|
129
|
+
</main>
|
|
130
|
+
|
|
131
|
+
<!-- Footer -->
|
|
132
|
+
<footer class="mt-auto bg-white border-t border-slate-200 py-6 text-center text-xs text-slate-500">
|
|
133
|
+
json-to-spec • KeshavSoft • MIT License
|
|
134
|
+
</footer>
|
|
135
|
+
|
|
136
|
+
<!-- Script loading: Resilient local/CDN import -->
|
|
137
|
+
<script type="module">
|
|
138
|
+
let compile;
|
|
139
|
+
try {
|
|
140
|
+
const mod = await import("./dist/min.js");
|
|
141
|
+
compile = mod.compile;
|
|
142
|
+
} catch {
|
|
143
|
+
const mod = await import("https://keshavsoft.github.io/json-to-spec/dist/v1/min.js");
|
|
144
|
+
compile = mod.compile;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
import { buildSpecElement } from "https://keshavsoft.github.io/json-to-dom/dist/v16/min.js";
|
|
148
|
+
|
|
149
|
+
const PRESETS = {
|
|
150
|
+
form: {
|
|
151
|
+
structure: {
|
|
152
|
+
"tagName": "div",
|
|
153
|
+
"attributes": {
|
|
154
|
+
"class": "card shadow-sm border border-slate-200 rounded-xl overflow-hidden max-w-xl mx-auto"
|
|
155
|
+
},
|
|
156
|
+
"children": [
|
|
157
|
+
{
|
|
158
|
+
"tagName": "div",
|
|
159
|
+
"attributes": { "class": "card-header bg-slate-100 px-6 py-4 border-b border-slate-200" },
|
|
160
|
+
"children": [
|
|
161
|
+
{ "tagName": "h2", "textContent": "Item Master Form", "attributes": { "class": "h5 mb-0 fw-bold text-slate-800" } },
|
|
162
|
+
{ "tagName": "p", "textContent": "Compiled via json-to-spec -> Rendered via json-to-dom", "attributes": { "class": "small text-muted mb-0" } }
|
|
163
|
+
]
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"tagName": "div",
|
|
167
|
+
"attributes": { "id": "form-body", "class": "card-body p-6 space-y-3" },
|
|
168
|
+
"children": [
|
|
169
|
+
{
|
|
170
|
+
"operation": "iterate",
|
|
171
|
+
"source": "columns",
|
|
172
|
+
"filter": { "isVisible": true },
|
|
173
|
+
"template": {
|
|
174
|
+
"tagName": "div",
|
|
175
|
+
"attributes": { "class": "row align-items-center g-2 mb-3" },
|
|
176
|
+
"children": [
|
|
177
|
+
{
|
|
178
|
+
"tagName": "label",
|
|
179
|
+
"textContent": "${title}",
|
|
180
|
+
"attributes": { "class": "col-sm-4 col-form-label text-sm-end mb-0 fw-semibold text-secondary small" }
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"tagName": "div",
|
|
184
|
+
"attributes": { "class": "col-sm-8" },
|
|
185
|
+
"children": [
|
|
186
|
+
{
|
|
187
|
+
"tagName": "input",
|
|
188
|
+
"attributes": {
|
|
189
|
+
"type": "text",
|
|
190
|
+
"name": "${field}",
|
|
191
|
+
"placeholder": "Enter ${title}...",
|
|
192
|
+
"class": "form-control"
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"tagName": "div",
|
|
204
|
+
"attributes": { "class": "card-footer bg-slate-50 px-6 py-3 border-t border-slate-200 d-flex justify-content-end gap-2" },
|
|
205
|
+
"children": [
|
|
206
|
+
{ "tagName": "button", "textContent": "Cancel", "attributes": { "type": "button", "class": "btn btn-outline-secondary btn-sm" } },
|
|
207
|
+
{ "tagName": "button", "textContent": "Save Item", "attributes": { "type": "submit", "class": "btn btn-primary btn-sm" } }
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
},
|
|
212
|
+
data: {
|
|
213
|
+
"columns": [
|
|
214
|
+
{ "columnName": "Category", "title": "Category", "field": "Category", "value": "Beverages", "isVisible": true },
|
|
215
|
+
{ "columnName": "ItemName", "title": "Item Name", "field": "ItemName", "value": "Green Tea", "isVisible": true },
|
|
216
|
+
{ "columnName": "Service", "title": "Service", "field": "Service", "value": "Packaging", "isVisible": true },
|
|
217
|
+
{ "columnName": "Rate", "title": "Rate", "field": "Rate", "value": 150, "isVisible": true },
|
|
218
|
+
{ "columnName": "pk", "title": "pk", "field": "pk", "isVisible": false }
|
|
219
|
+
],
|
|
220
|
+
"Category": "Beverages",
|
|
221
|
+
"ItemName": "Green Tea",
|
|
222
|
+
"Service": "Packaging",
|
|
223
|
+
"Rate": 150
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
table: {
|
|
227
|
+
structure: {
|
|
228
|
+
"tagName": "div",
|
|
229
|
+
"attributes": { "class": "card shadow-sm border border-slate-200 rounded-xl overflow-hidden max-w-4xl mx-auto" },
|
|
230
|
+
"children": [
|
|
231
|
+
{
|
|
232
|
+
"tagName": "div",
|
|
233
|
+
"attributes": { "class": "card-header bg-slate-100 px-6 py-4 border-b border-slate-200 d-flex justify-content-between align-items-center" },
|
|
234
|
+
"children": [
|
|
235
|
+
{
|
|
236
|
+
"tagName": "div",
|
|
237
|
+
"children": [
|
|
238
|
+
{ "tagName": "h2", "textContent": "Item Inventory Table", "attributes": { "class": "h5 mb-0 fw-bold text-slate-800" } },
|
|
239
|
+
{ "tagName": "p", "textContent": "Rendered via nested iterations (thead & tbody)", "attributes": { "class": "small text-muted mb-0" } }
|
|
240
|
+
]
|
|
241
|
+
},
|
|
242
|
+
{ "tagName": "span", "textContent": "${summary.totalItems} Items", "attributes": { "class": "badge bg-indigo-600 text-white font-mono px-3 py-1.5" } }
|
|
243
|
+
]
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"tagName": "div",
|
|
247
|
+
"attributes": { "class": "table-responsive" },
|
|
248
|
+
"children": [
|
|
249
|
+
{
|
|
250
|
+
"tagName": "table",
|
|
251
|
+
"attributes": { "class": "table table-hover table-striped align-middle mb-0" },
|
|
252
|
+
"children": [
|
|
253
|
+
{
|
|
254
|
+
"tagName": "thead",
|
|
255
|
+
"attributes": { "class": "table-light" },
|
|
256
|
+
"children": [
|
|
257
|
+
{
|
|
258
|
+
"tagName": "tr",
|
|
259
|
+
"children": [
|
|
260
|
+
{ "tagName": "th", "textContent": "#", "attributes": { "class": "text-center py-3", "style": "width: 50px;" } },
|
|
261
|
+
{
|
|
262
|
+
"operation": "iterate",
|
|
263
|
+
"source": "columns",
|
|
264
|
+
"filter": { "isVisible": true },
|
|
265
|
+
"template": {
|
|
266
|
+
"tagName": "th",
|
|
267
|
+
"textContent": "${title}",
|
|
268
|
+
"attributes": { "class": "text-start py-3" }
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
]
|
|
272
|
+
}
|
|
273
|
+
]
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"tagName": "tbody",
|
|
277
|
+
"children": [
|
|
278
|
+
{
|
|
279
|
+
"operation": "iterate",
|
|
280
|
+
"source": "rows",
|
|
281
|
+
"template": {
|
|
282
|
+
"tagName": "tr",
|
|
283
|
+
"children": [
|
|
284
|
+
{ "tagName": "td", "textContent": "${$number}", "attributes": { "class": "text-center font-mono small text-muted py-2.5" } },
|
|
285
|
+
{
|
|
286
|
+
"operation": "iterate",
|
|
287
|
+
"source": "columns",
|
|
288
|
+
"filter": { "isVisible": true },
|
|
289
|
+
"template": {
|
|
290
|
+
"tagName": "td",
|
|
291
|
+
"textContent": "${value}",
|
|
292
|
+
"attributes": { "class": "text-start py-2.5" }
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
]
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"tagName": "tfoot",
|
|
302
|
+
"attributes": { "class": "table-light border-top" },
|
|
303
|
+
"children": [
|
|
304
|
+
{
|
|
305
|
+
"tagName": "tr",
|
|
306
|
+
"children": [
|
|
307
|
+
{
|
|
308
|
+
"tagName": "td",
|
|
309
|
+
"textContent": "Summary: Total Items: ${summary.totalItems} | Avg Rate: ?${summary.avgRate}",
|
|
310
|
+
"attributes": { "colspan": "6", "class": "small text-muted py-2.5 px-4 fw-semibold" }
|
|
311
|
+
}
|
|
312
|
+
]
|
|
313
|
+
}
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
]
|
|
317
|
+
}
|
|
318
|
+
]
|
|
319
|
+
}
|
|
320
|
+
]
|
|
321
|
+
},
|
|
322
|
+
data: {
|
|
323
|
+
"columns": [
|
|
324
|
+
{ "title": "Category", "field": "Category", "isVisible": true },
|
|
325
|
+
{ "title": "Item Name", "field": "ItemName", "isVisible": true },
|
|
326
|
+
{ "title": "Service", "field": "Service", "isVisible": true },
|
|
327
|
+
{ "title": "Rate", "field": "Rate", "isVisible": true },
|
|
328
|
+
{ "title": "PK", "field": "pk", "isVisible": false }
|
|
329
|
+
],
|
|
330
|
+
"rows": [
|
|
331
|
+
{ "pk": "1", "Category": "Beverages", "ItemName": "Green Tea", "Service": "Packaging", "Rate": 150 },
|
|
332
|
+
{ "pk": "2", "Category": "Beverages", "ItemName": "Espresso Coffee", "Service": "Grinding", "Rate": 220 },
|
|
333
|
+
{ "pk": "3", "Category": "Bakery", "ItemName": "Almond Cookies", "Service": "Baking", "Rate": 95 }
|
|
334
|
+
],
|
|
335
|
+
"summary": { "totalItems": 3, "avgRate": 155 }
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
simple: {
|
|
339
|
+
structure: {
|
|
340
|
+
"tagName": "div",
|
|
341
|
+
"attributes": { "class": "card p-4 border border-indigo-200 rounded-2xl max-w-sm mx-auto text-center bg-indigo-50/50" },
|
|
342
|
+
"children": [
|
|
343
|
+
{ "tagName": "h3", "textContent": "${greeting}", "attributes": { "class": "h5 fw-bold text-indigo-900" } },
|
|
344
|
+
{ "tagName": "p", "textContent": "${message}", "attributes": { "class": "small text-indigo-700 mb-3" } },
|
|
345
|
+
{
|
|
346
|
+
"operation": "iterate",
|
|
347
|
+
"source": "badges",
|
|
348
|
+
"template": {
|
|
349
|
+
"tagName": "span",
|
|
350
|
+
"textContent": "${title}",
|
|
351
|
+
"attributes": { "class": "badge bg-indigo-600 text-white me-1 mb-1 font-mono text-xs" }
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
]
|
|
355
|
+
},
|
|
356
|
+
data: {
|
|
357
|
+
"greeting": "Hello Declarative UI",
|
|
358
|
+
"message": "Pure JSON compiled into valid DOM spec",
|
|
359
|
+
"badges": [
|
|
360
|
+
{ "title": "Zero Dependencies" },
|
|
361
|
+
{ "title": "Universal Traversal" },
|
|
362
|
+
{ "title": "json-to-dom Compatible" }
|
|
363
|
+
]
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const structEditor = document.getElementById("structure-editor");
|
|
369
|
+
const dataEditor = document.getElementById("data-editor");
|
|
370
|
+
const specOutput = document.getElementById("spec-output");
|
|
371
|
+
const app = document.getElementById("app");
|
|
372
|
+
const compileStatus = document.getElementById("compile-status");
|
|
373
|
+
|
|
374
|
+
function executeCompile() {
|
|
375
|
+
try {
|
|
376
|
+
const structure = JSON.parse(structEditor.value);
|
|
377
|
+
const data = JSON.parse(dataEditor.value);
|
|
378
|
+
|
|
379
|
+
const t0 = performance.now();
|
|
380
|
+
const compiledSpec = compile({ inStructure: structure, inData: data });
|
|
381
|
+
const elapsed = (performance.now() - t0).toFixed(2);
|
|
382
|
+
|
|
383
|
+
specOutput.textContent = JSON.stringify(compiledSpec, null, 2);
|
|
384
|
+
|
|
385
|
+
app.innerHTML = "";
|
|
386
|
+
const domNode = buildSpecElement({ inSpec: compiledSpec });
|
|
387
|
+
if (domNode) {
|
|
388
|
+
if (Array.isArray(domNode)) app.append(...domNode);
|
|
389
|
+
else app.appendChild(domNode);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
compileStatus.innerHTML = `<span class="w-2 h-2 rounded-full bg-emerald-500"></span> Compiled in ${elapsed}ms`;
|
|
393
|
+
compileStatus.className = "text-xs font-mono text-emerald-600 font-semibold flex items-center gap-1.5";
|
|
394
|
+
} catch (err) {
|
|
395
|
+
console.error(err);
|
|
396
|
+
compileStatus.innerHTML = `<span class="w-2 h-2 rounded-full bg-rose-500"></span> Error: ${err.message}`;
|
|
397
|
+
compileStatus.className = "text-xs font-mono text-rose-600 font-semibold flex items-center gap-1.5";
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function loadPreset(key) {
|
|
402
|
+
const p = PRESETS[key];
|
|
403
|
+
if (!p) return;
|
|
404
|
+
structEditor.value = JSON.stringify(p.structure, null, 2);
|
|
405
|
+
dataEditor.value = JSON.stringify(p.data, null, 2);
|
|
406
|
+
executeCompile();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
document.getElementById("preset-form").addEventListener("click", () => loadPreset("form"));
|
|
410
|
+
document.getElementById("preset-table").addEventListener("click", () => loadPreset("table"));
|
|
411
|
+
document.getElementById("preset-simple").addEventListener("click", () => loadPreset("simple"));
|
|
412
|
+
document.getElementById("compile-btn").addEventListener("click", executeCompile);
|
|
413
|
+
|
|
414
|
+
document.getElementById("copy-spec-btn").addEventListener("click", () => {
|
|
415
|
+
navigator.clipboard.writeText(specOutput.textContent).then(() => {
|
|
416
|
+
const btn = document.getElementById("copy-spec-btn");
|
|
417
|
+
btn.textContent = "Copied!";
|
|
418
|
+
setTimeout(() => { btn.textContent = "Copy Spec"; }, 1500);
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// Initial preset
|
|
423
|
+
loadPreset("form");
|
|
424
|
+
</script>
|
|
425
|
+
</body>
|
|
426
|
+
</html>
|