check-rule-mate 0.5.6 → 0.5.9
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 +4 -4
- package/bin/commands/generate-docs-playground-experimental.js +639 -0
- package/bin/commands/generate-docs.js +435 -0
- package/bin/{verify-templates.js → commands/verify-templates.js} +8 -32
- package/bin/index.js +50 -0
- package/bin/utils/args.js +10 -0
- package/{src/index.d.ts → index.d.ts} +36 -18
- package/package.json +2 -4
- package/bin/generate-docs-playground-experimental.js +0 -617
- package/bin/generate-docs.js +0 -434
- /package/{src/types.js → types.js} +0 -0
package/README.md
CHANGED
|
@@ -294,7 +294,7 @@ await validator.validate();
|
|
|
294
294
|
To use that it is simple, you only need to run this command:
|
|
295
295
|
|
|
296
296
|
```bash
|
|
297
|
-
npx check-rule-mate
|
|
297
|
+
npx check-rule-mate docs --rules {rules path} --schemas {schemas path} --errors {errors path} --out {file.html}
|
|
298
298
|
```
|
|
299
299
|
|
|
300
300
|
This will generate a HTML file containing the rules, schemas and errors.
|
|
@@ -304,7 +304,7 @@ This will generate a HTML file containing the rules, schemas and errors.
|
|
|
304
304
|
> ⚠️ The playground executes bundled client-side JavaScript.
|
|
305
305
|
> Do not use untrusted validation code.
|
|
306
306
|
|
|
307
|
-
The **
|
|
307
|
+
The **Documentation Playground** allows you to generate an interactive HTML page where you can **test your schemas, rules, and validators directly in the browser**.
|
|
308
308
|
|
|
309
309
|
This feature is **experimental** and intended mainly for development and exploration purposes. It bundles your validation logic into a client-side format, enabling real-time validation without any backend setup.
|
|
310
310
|
|
|
@@ -326,7 +326,7 @@ npm install esbuild
|
|
|
326
326
|
|
|
327
327
|
Run the playground generator using the CLI:
|
|
328
328
|
```bash
|
|
329
|
-
npx check-rule-mate
|
|
329
|
+
npx check-rule-mate docs:playground \
|
|
330
330
|
--rules {rules-path} \
|
|
331
331
|
--schemas {schemas-path} \
|
|
332
332
|
--errors {errors-path} \
|
|
@@ -365,7 +365,7 @@ You can **auto check** if your template it is working properly with all necessar
|
|
|
365
365
|
It is works with: `Schemas`, `Rules` and `Errors`
|
|
366
366
|
|
|
367
367
|
```bash
|
|
368
|
-
npx check-rule-mate
|
|
368
|
+
npx check-rule-mate verify --rules {rules path} --schemas {schemas path} --errors {errors path}
|
|
369
369
|
```
|
|
370
370
|
|
|
371
371
|
If everything it is **working properly** this should be your message:
|
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* check-rule-mate — Auto Documentation Generator With Playground
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const process = require('process');
|
|
8
|
+
const { parseArgs } = require('../utils/args.js');
|
|
9
|
+
let esbuild;
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
esbuild = require('esbuild');
|
|
13
|
+
} catch {
|
|
14
|
+
console.warn(
|
|
15
|
+
'[check-rule-mate] esbuild not found. ' +
|
|
16
|
+
'Install it to enable playground generation.'
|
|
17
|
+
);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const generateDocsPlayground = function ({ rulesArg, schemasArg, errorsArg, optionsArg, outArg }) {
|
|
22
|
+
const RULES_DIR = rulesArg;
|
|
23
|
+
const SCHEMAS_DIR = schemasArg;
|
|
24
|
+
const ERRORS_DIR = errorsArg;
|
|
25
|
+
const OPTIONS_DIR = optionsArg;
|
|
26
|
+
const OUTPUT = outArg || "check-rule-mate-docs.html";
|
|
27
|
+
const OUTPUT_DIR = path.dirname(OUTPUT);
|
|
28
|
+
|
|
29
|
+
const OPTIONS = OPTIONS_DIR ? JSON.parse(fs.readFileSync(OPTIONS_DIR, "utf-8")) : null;
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if (OPTIONS) {
|
|
33
|
+
let entryPointFile = Object.keys(OPTIONS.validators).map((key) => `var ${key} = require('${OPTIONS.validators[key]}')`).join('\n');
|
|
34
|
+
entryPointFile += `\n window.validatorHelpers = {${Object.keys(OPTIONS.validators).map((key) => `${key}: ${key}`)}}`
|
|
35
|
+
|
|
36
|
+
fs.writeFileSync(`./check-rule-mate.validators.docs.js`, entryPointFile);
|
|
37
|
+
|
|
38
|
+
esbuild.build({
|
|
39
|
+
entryPoints: ['./node_modules/check-rule-mate/src/main.js'],
|
|
40
|
+
bundle: true,
|
|
41
|
+
outfile: `${OUTPUT_DIR}/check-rule-mate.js`,
|
|
42
|
+
platform: 'browser',
|
|
43
|
+
target: 'node16',
|
|
44
|
+
format: 'iife',
|
|
45
|
+
minify: false
|
|
46
|
+
}).then(() => {
|
|
47
|
+
console.log('Build completed successfully: check-rule-mate.js');
|
|
48
|
+
|
|
49
|
+
const checkRuleMateFIle = fs.readFileSync(`${OUTPUT_DIR}/check-rule-mate.js`, 'utf-8');
|
|
50
|
+
const lines = checkRuleMateFIle.split('\n')
|
|
51
|
+
lines.shift();
|
|
52
|
+
lines.splice(lines.length - 2, 1)
|
|
53
|
+
fs.writeFileSync(`${OUTPUT_DIR}/check-rule-mate.js`, lines.join('\n'))
|
|
54
|
+
}).catch(() => process.exit(1));
|
|
55
|
+
|
|
56
|
+
esbuild.build({
|
|
57
|
+
entryPoints: [`./check-rule-mate.validators.docs.js`],
|
|
58
|
+
bundle: true,
|
|
59
|
+
outfile: `${OUTPUT_DIR}/index.js`,
|
|
60
|
+
platform: 'browser',
|
|
61
|
+
target: 'node16',
|
|
62
|
+
format: 'iife',
|
|
63
|
+
minify: false
|
|
64
|
+
}).then(() => {
|
|
65
|
+
console.log('Build completed successfully: index.js');
|
|
66
|
+
fs.rmSync('./check-rule-mate.validators.docs.js')
|
|
67
|
+
}).catch(() => process.exit(1));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if (!RULES_DIR || !SCHEMAS_DIR) {
|
|
72
|
+
console.error(`
|
|
73
|
+
Usage:
|
|
74
|
+
npx check-rule-mate docs:playground --rules ./rules --schemas ./schemas --errors ./errors --out docs.html
|
|
75
|
+
`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* ---------------------------------------
|
|
80
|
+
* HELPERS
|
|
81
|
+
* -------------------------------------*/
|
|
82
|
+
const readJSONFiles = (dir) => {
|
|
83
|
+
return fs.readdirSync(dir)
|
|
84
|
+
.filter(f => f.endsWith(".json"))
|
|
85
|
+
.map(file => ({
|
|
86
|
+
name: file.replace(".json", ""),
|
|
87
|
+
content: JSON.parse(fs.readFileSync(path.join(dir, file), "utf-8"))
|
|
88
|
+
}));
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const rulesFiles = readJSONFiles(RULES_DIR);
|
|
92
|
+
const schemasFiles = readJSONFiles(SCHEMAS_DIR);
|
|
93
|
+
const errorsFiles = readJSONFiles(ERRORS_DIR);
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
/* ---------------------------------------
|
|
97
|
+
* INDEX RELATIONSHIPS
|
|
98
|
+
* -------------------------------------*/
|
|
99
|
+
const ruleUsageMap = {};
|
|
100
|
+
const errorUsageMap = {}
|
|
101
|
+
|
|
102
|
+
schemasFiles.forEach(schemaFile => {
|
|
103
|
+
Object.entries(schemaFile.content).forEach(([field, config]) => {
|
|
104
|
+
const ruleName = config.rule.split("--")[0];
|
|
105
|
+
if (!ruleUsageMap[ruleName]) ruleUsageMap[ruleName] = [];
|
|
106
|
+
ruleUsageMap[ruleName].push({
|
|
107
|
+
schema: schemaFile.name,
|
|
108
|
+
field,
|
|
109
|
+
rule: config.rule
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
rulesFiles.forEach(rulesFile => {
|
|
115
|
+
Object.entries(rulesFile.content).forEach(([field, config]) => {
|
|
116
|
+
Object.entries(config.error).forEach(([key, value]) => {
|
|
117
|
+
const errorName = value;
|
|
118
|
+
if (!errorUsageMap[errorName]) errorUsageMap[errorName] = [];
|
|
119
|
+
errorUsageMap[errorName].push({
|
|
120
|
+
file: rulesFile.name,
|
|
121
|
+
field,
|
|
122
|
+
error: value
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
if (config?.modifier) {
|
|
126
|
+
Object.entries(config.modifier).forEach(([modifierKey, modifier]) => {
|
|
127
|
+
if (modifier?.error) {
|
|
128
|
+
Object.entries(modifier.error).forEach(([key, value]) => {
|
|
129
|
+
const errorName = value;
|
|
130
|
+
if (!errorUsageMap[errorName]) errorUsageMap[errorName] = [];
|
|
131
|
+
errorUsageMap[errorName].push({
|
|
132
|
+
file: rulesFile.name,
|
|
133
|
+
field,
|
|
134
|
+
error: value
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
/* ---------------------------------------
|
|
145
|
+
* HTML GENERATION
|
|
146
|
+
* -------------------------------------*/
|
|
147
|
+
const html = `
|
|
148
|
+
<!DOCTYPE html>
|
|
149
|
+
<html lang="en">
|
|
150
|
+
<head>
|
|
151
|
+
<meta charset="UTF-8" />
|
|
152
|
+
<title>check-rule-mate — Documentation</title>
|
|
153
|
+
<style>
|
|
154
|
+
${generateCSS()}
|
|
155
|
+
</style>
|
|
156
|
+
${OPTIONS ? `
|
|
157
|
+
<script src="./check-rule-mate.js" defer></script>
|
|
158
|
+
<script src="./index.js" defer></script>`
|
|
159
|
+
: ''}
|
|
160
|
+
</head>
|
|
161
|
+
<body>
|
|
162
|
+
|
|
163
|
+
<aside class="sidebar">
|
|
164
|
+
<input id="search" placeholder="Search..." />
|
|
165
|
+
|
|
166
|
+
<h3>Schemas</h3>
|
|
167
|
+
${schemasFiles.map(s => `
|
|
168
|
+
<a href="#schema-${s.name}">${s.name}</a>
|
|
169
|
+
`).join("")}
|
|
170
|
+
|
|
171
|
+
<h3>Rules</h3>
|
|
172
|
+
${rulesFiles.map(rf =>
|
|
173
|
+
Object.keys(rf.content).map(rule => `
|
|
174
|
+
<a href="#rule-${rule}">${rf.name} - ${rule}</a>
|
|
175
|
+
`).join("")
|
|
176
|
+
).join("")}
|
|
177
|
+
|
|
178
|
+
<h3>Errors</h3>
|
|
179
|
+
${errorsFiles.map(errorFile =>
|
|
180
|
+
Object.keys(errorFile.content).map(error => `
|
|
181
|
+
<a href="#error-${error}">${errorFile.name} - ${error}</a>
|
|
182
|
+
`).join("")
|
|
183
|
+
).join("")}
|
|
184
|
+
</aside>
|
|
185
|
+
|
|
186
|
+
<main>
|
|
187
|
+
<section>
|
|
188
|
+
<h1>check-rule-mate</h1>
|
|
189
|
+
<p class="muted">
|
|
190
|
+
Visual documentation of validation rules and schemas.
|
|
191
|
+
</p>
|
|
192
|
+
</section>
|
|
193
|
+
|
|
194
|
+
${renderSchemas(schemasFiles)}
|
|
195
|
+
${renderRules(rulesFiles, ruleUsageMap)}
|
|
196
|
+
${renderErrors(errorsFiles, errorUsageMap)}
|
|
197
|
+
</main>
|
|
198
|
+
|
|
199
|
+
<script>
|
|
200
|
+
${generateClientJS()}
|
|
201
|
+
</script>
|
|
202
|
+
|
|
203
|
+
</body>
|
|
204
|
+
</html>
|
|
205
|
+
`;
|
|
206
|
+
|
|
207
|
+
fs.writeFileSync(OUTPUT, html);
|
|
208
|
+
console.log(`✔ Documentation generated at ${OUTPUT}`);
|
|
209
|
+
|
|
210
|
+
/* ---------------------------------------
|
|
211
|
+
* RENDERERS
|
|
212
|
+
* -------------------------------------*/
|
|
213
|
+
function renderSchemas(schemas) {
|
|
214
|
+
return schemas.map(schema => `
|
|
215
|
+
<section id="schema-${schema.name}" class="card">
|
|
216
|
+
<h2>Schema: ${schema.name}</h2>
|
|
217
|
+
|
|
218
|
+
<table>
|
|
219
|
+
<thead>
|
|
220
|
+
<tr>
|
|
221
|
+
<th>Field</th>
|
|
222
|
+
<th>Rule</th>
|
|
223
|
+
<th class="text-center">Required</th>
|
|
224
|
+
<th class="text-center">Cache</th>
|
|
225
|
+
</tr>
|
|
226
|
+
</thead>
|
|
227
|
+
<tbody>
|
|
228
|
+
${Object.entries(schema.content).map(([field, cfg]) => `
|
|
229
|
+
<tr>
|
|
230
|
+
<td>${field}</td>
|
|
231
|
+
<td>
|
|
232
|
+
<a href="#rule-${cfg.rule.split("--")[0]}">
|
|
233
|
+
${cfg.rule}
|
|
234
|
+
</a>
|
|
235
|
+
</td>
|
|
236
|
+
<td class="text-center">${cfg.required ? "✔" : "optional"}</td>
|
|
237
|
+
<td class="text-center">${cfg.cache === false ? "off" : "✔"}</td>
|
|
238
|
+
</tr>
|
|
239
|
+
`).join("")}
|
|
240
|
+
</tbody>
|
|
241
|
+
</table>
|
|
242
|
+
${OPTIONS ? `
|
|
243
|
+
<details class="schema-test">
|
|
244
|
+
<summary>Schema Test (Experimental)</summary>
|
|
245
|
+
<div class="schema-test-container">
|
|
246
|
+
<form id="schema:${schema.name}" data-form="schema--${schema.name}">
|
|
247
|
+
${Object.entries(schema.content).map(([field, cfg]) => {
|
|
248
|
+
const attributesHTML = cfg.attributesHTML ? Object.keys(cfg.attributesHTML).map((key) => `${key}="${cfg?.attributesHTML[key]}"`).join(' ') : null
|
|
249
|
+
return `
|
|
250
|
+
<div style="display: flex; flex-direction: column;">
|
|
251
|
+
<label for="${field}">${field} (${cfg.rule})</label>
|
|
252
|
+
<input name="${field}" ${cfg.required ? 'required': ''} ${attributesHTML}/>
|
|
253
|
+
</div>
|
|
254
|
+
`}).join("")}
|
|
255
|
+
<div style="display: flex; flex-direction: column;">
|
|
256
|
+
<label for="check-rule-mate-validators">VALIDATORS</label>
|
|
257
|
+
<select name="check-rule-mate-validators" required>
|
|
258
|
+
${
|
|
259
|
+
OPTIONS?.validators ? Object.keys(OPTIONS.validators).map((key) => `
|
|
260
|
+
<option value=${key}>${key}</option>
|
|
261
|
+
`)
|
|
262
|
+
: ''
|
|
263
|
+
}
|
|
264
|
+
</select>
|
|
265
|
+
</div>
|
|
266
|
+
<div style="display: flex; flex-direction: column;">
|
|
267
|
+
<label for="check-rule-mate-rules">RULES</label>
|
|
268
|
+
<select name="check-rule-mate-rules" required>
|
|
269
|
+
${
|
|
270
|
+
rulesFiles ? rulesFiles.map((rulesFile) => `
|
|
271
|
+
<option value=${rulesFile.name}>${rulesFile.name}</option>
|
|
272
|
+
`)
|
|
273
|
+
: ''
|
|
274
|
+
}
|
|
275
|
+
</select>
|
|
276
|
+
</div>
|
|
277
|
+
<button type="submit">Validate Schema</button>
|
|
278
|
+
</form>
|
|
279
|
+
<div class="schema-test-result">
|
|
280
|
+
<span>Result:</span>
|
|
281
|
+
<textarea id="textarea-schema-${schema.name}" readonly></textarea>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
</details>
|
|
285
|
+
` : ''}
|
|
286
|
+
</section>
|
|
287
|
+
`).join("");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function renderRules(rulesFiles, usageMap) {
|
|
291
|
+
return rulesFiles.map(file =>
|
|
292
|
+
Object.entries(file.content).map(([ruleName, rule]) => `
|
|
293
|
+
<section id="rule-${ruleName}" class="card">
|
|
294
|
+
<h2>Rule: ${ruleName} (${file.name})</h2>
|
|
295
|
+
${rule?.docs?.description ?
|
|
296
|
+
`<p>${rule.docs.description}</p>`
|
|
297
|
+
: ''}
|
|
298
|
+
|
|
299
|
+
<h3>Validation Flow</h3>
|
|
300
|
+
<div class="flow">
|
|
301
|
+
${rule.validate.map(v => `
|
|
302
|
+
<div class="flow-step">${v}</div>
|
|
303
|
+
<div class="flow-arrow">→</div>
|
|
304
|
+
`).join("")}
|
|
305
|
+
<div class="flow-step success">valid</div>
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
<h3>Error Codes</h3>
|
|
309
|
+
${renderRulesErrors(rule.error)}
|
|
310
|
+
|
|
311
|
+
${rule.modifier ? `
|
|
312
|
+
<h3>Modifiers</h3>
|
|
313
|
+
${Object.entries(rule.modifier).map(([mod, modRule]) => `
|
|
314
|
+
<div class="modifier">
|
|
315
|
+
<span class="tag modifier">${mod}</span>
|
|
316
|
+
${modRule?.docs?.description ? `<p>${modRule.docs.description}</p>` : ''}
|
|
317
|
+
|
|
318
|
+
<div class="flow">
|
|
319
|
+
${modRule.validate.map(v => `
|
|
320
|
+
<div class="flow-step">${v}</div>
|
|
321
|
+
<div class="flow-arrow">→</div>
|
|
322
|
+
`).join("")}
|
|
323
|
+
<div class="flow-step success">valid</div>
|
|
324
|
+
</div>
|
|
325
|
+
|
|
326
|
+
<h4>Error Codes</h4>
|
|
327
|
+
${renderRulesErrors(modRule.error)}
|
|
328
|
+
</div>
|
|
329
|
+
`).join("")}
|
|
330
|
+
` : ""}
|
|
331
|
+
|
|
332
|
+
<h3>Used by Schemas</h3>
|
|
333
|
+
<ul>
|
|
334
|
+
${(usageMap[ruleName] || []).map(u =>
|
|
335
|
+
`<li>${u.schema} → <strong>${u.field}</strong> (${u.rule})</li>`
|
|
336
|
+
).join("") || "<li>Not used</li>"}
|
|
337
|
+
</ul>
|
|
338
|
+
|
|
339
|
+
${rule?.docs?.notes ?
|
|
340
|
+
`
|
|
341
|
+
<h3>Notes</h3>
|
|
342
|
+
<div class="rule-notes">${rule?.docs?.notes}</div>
|
|
343
|
+
`
|
|
344
|
+
: ''}
|
|
345
|
+
</section>
|
|
346
|
+
`).join("")
|
|
347
|
+
).join("");
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function renderRulesErrors(errors = {}) {
|
|
351
|
+
return `
|
|
352
|
+
<ul>
|
|
353
|
+
${Object.entries(errors).map(([k, v]) =>
|
|
354
|
+
`<li><a href="#error-${v.split('.')[0]}" class="tag error">${k}</a> ${v}</li>`
|
|
355
|
+
).join("")}
|
|
356
|
+
</ul>
|
|
357
|
+
`;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function renderErrors(errorFiles, usageMap) {
|
|
361
|
+
return errorFiles.map(file =>
|
|
362
|
+
Object.entries(file.content).map(([errorName, errors]) => `
|
|
363
|
+
<section id="error-${errorName}" class="card">
|
|
364
|
+
<h2>Error: ${errorName} (${file.name})</h2>
|
|
365
|
+
<ul>
|
|
366
|
+
${Object.entries(errors).map(([key, value]) => `
|
|
367
|
+
<li><span class="tag error">${key}</span> ${value} </li>
|
|
368
|
+
`).join('')}
|
|
369
|
+
</ul>
|
|
370
|
+
|
|
371
|
+
<h3>Used by Rules</h3>
|
|
372
|
+
<ul>
|
|
373
|
+
${Object.entries(errors).map(([key, value]) => `
|
|
374
|
+
${(usageMap[`${errorName}.${key}`] || []).map(u =>
|
|
375
|
+
`<li>${u.file} → <strong>${u.field}</strong> (${u.error})</li>`
|
|
376
|
+
).join("") || "<li>Not used</li>"}
|
|
377
|
+
`).join('')}
|
|
378
|
+
</ul>
|
|
379
|
+
</section>
|
|
380
|
+
`).join('')).join('');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* ---------------------------------------
|
|
384
|
+
* CSS
|
|
385
|
+
* -------------------------------------*/
|
|
386
|
+
function generateCSS() {
|
|
387
|
+
return `
|
|
388
|
+
body {
|
|
389
|
+
margin: 0;
|
|
390
|
+
font-family: Inter, system-ui, sans-serif;
|
|
391
|
+
display: grid;
|
|
392
|
+
grid-template-columns: 280px 1fr;
|
|
393
|
+
background: #0f172a;
|
|
394
|
+
color: #e5e7eb;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.sidebar {
|
|
398
|
+
padding: 16px;
|
|
399
|
+
background: #020617;
|
|
400
|
+
overflow-y: auto;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.sidebar input {
|
|
404
|
+
width: 100%;
|
|
405
|
+
padding: 8px;
|
|
406
|
+
border-radius: 6px;
|
|
407
|
+
border: none;
|
|
408
|
+
margin-bottom: 16px;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.sidebar a {
|
|
412
|
+
display: block;
|
|
413
|
+
padding: 6px 10px;
|
|
414
|
+
color: #e5e7eb;
|
|
415
|
+
text-decoration: none;
|
|
416
|
+
border-radius: 6px;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.sidebar a:hover {
|
|
420
|
+
background: rgba(56,189,248,0.2);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
a {
|
|
424
|
+
color: #e5e7eb;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
main {
|
|
428
|
+
padding: 32px;
|
|
429
|
+
overflow-y: auto;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.card {
|
|
433
|
+
background: #020617;
|
|
434
|
+
border-radius: 12px;
|
|
435
|
+
padding: 24px;
|
|
436
|
+
margin-bottom: 32px;
|
|
437
|
+
border: 1px solid rgba(255,255,255,0.05);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.flow {
|
|
441
|
+
display: flex;
|
|
442
|
+
align-items: center;
|
|
443
|
+
gap: 12px;
|
|
444
|
+
flex-wrap: wrap;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.flow-step {
|
|
448
|
+
padding: 10px 14px;
|
|
449
|
+
border-radius: 8px;
|
|
450
|
+
background: rgba(56,189,248,0.15);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.flow-arrow {
|
|
454
|
+
opacity: 0.5;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
.success {
|
|
458
|
+
background: rgba(34,197,94,0.2);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.tag {
|
|
462
|
+
padding: 4px 8px;
|
|
463
|
+
border-radius: 999px;
|
|
464
|
+
font-size: 14px;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.tag.error {
|
|
468
|
+
display: inline-block;
|
|
469
|
+
margin-bottom: 8px;
|
|
470
|
+
background: rgba(239,68,68,0.2);
|
|
471
|
+
text-decoration: none;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.tag.modifier {
|
|
475
|
+
display: inline-block;
|
|
476
|
+
font-size: 16px;
|
|
477
|
+
margin-bottom: 8px;
|
|
478
|
+
font-weight: 600;
|
|
479
|
+
background: rgba(167,139,250,0.2);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
table {
|
|
483
|
+
width: 100%;
|
|
484
|
+
border-collapse: collapse;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
td, th {
|
|
488
|
+
padding: 8px;
|
|
489
|
+
border-bottom: 1px solid rgba(255,255,255,0.05);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
th {
|
|
493
|
+
text-align: left;
|
|
494
|
+
border-bottom: 1px solid #f4f4f4;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.rule-notes {
|
|
498
|
+
padding: 16px;
|
|
499
|
+
color: black;
|
|
500
|
+
background: #f4f4f4;
|
|
501
|
+
border-radius: 12px;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.text-center {
|
|
505
|
+
text-align: center;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.schema-test {
|
|
509
|
+
margin-top: 32px;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.schema-test-container {
|
|
513
|
+
display: flex;
|
|
514
|
+
width: 100%;
|
|
515
|
+
margin: 32px 0 24px 0;
|
|
516
|
+
gap: 40px;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.schema-test-container > * {
|
|
520
|
+
width: 100%;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.schema-test-result span {
|
|
524
|
+
display: block;
|
|
525
|
+
font-size: 20px;
|
|
526
|
+
font-weight: 600;
|
|
527
|
+
margin-bottom: 16px;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.schema-test-result textarea {
|
|
531
|
+
width: 100%;
|
|
532
|
+
height: 80%;
|
|
533
|
+
color: #f4f4f4;
|
|
534
|
+
background-color: #111111;
|
|
535
|
+
border: none;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
button {
|
|
539
|
+
cursor: pointer;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
form button[type="submit"] {
|
|
543
|
+
width: 100%;
|
|
544
|
+
padding: 16px 24px;
|
|
545
|
+
min-width: 120px;
|
|
546
|
+
min-height: 48px;
|
|
547
|
+
margin-top: 16px;
|
|
548
|
+
font-size: 16px;
|
|
549
|
+
font-weight: 600;
|
|
550
|
+
color: #444444;
|
|
551
|
+
background-color: #eede91;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
label {
|
|
555
|
+
margin-bottom: 4px;
|
|
556
|
+
font-size: 16px;
|
|
557
|
+
color: #e5e7eb;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
.schema-test input,
|
|
561
|
+
.schema-test select {
|
|
562
|
+
min-height: 28px;
|
|
563
|
+
padding: 4px 8px 4px 8px;
|
|
564
|
+
margin-bottom: 16px;
|
|
565
|
+
font-size: 16px;
|
|
566
|
+
color: #e5e7eb;
|
|
567
|
+
background-color: transparent;
|
|
568
|
+
border: none;
|
|
569
|
+
border-bottom: 1px solid white;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.schema-test select option {
|
|
573
|
+
background-color: #020617;
|
|
574
|
+
color: #e5e7eb;
|
|
575
|
+
}
|
|
576
|
+
`;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/* ---------------------------------------
|
|
580
|
+
* CLIENT JS
|
|
581
|
+
* -------------------------------------*/
|
|
582
|
+
function generateClientJS() {
|
|
583
|
+
return `
|
|
584
|
+
const search = document.getElementById("search");
|
|
585
|
+
search.addEventListener("input", e => {
|
|
586
|
+
const value = e.target.value.toLowerCase();
|
|
587
|
+
document.querySelectorAll("section.card").forEach(card => {
|
|
588
|
+
card.style.display = card.innerText.toLowerCase().includes(value)
|
|
589
|
+
? ""
|
|
590
|
+
: "none";
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
${OPTIONS ? `
|
|
595
|
+
setTimeout(() => {
|
|
596
|
+
const rulesFiles = ${JSON.stringify(rulesFiles)}
|
|
597
|
+
const schemasFiles = ${JSON.stringify(schemasFiles)}
|
|
598
|
+
|
|
599
|
+
const formElements = document.querySelectorAll('form[data-form]');
|
|
600
|
+
formElements.forEach((formElement) => {
|
|
601
|
+
formElement.addEventListener('submit', async (e) => {
|
|
602
|
+
e.preventDefault();
|
|
603
|
+
const inputElements = formElement.querySelectorAll('input, select');
|
|
604
|
+
const formName = formElement.dataset.form.split('--')[1];
|
|
605
|
+
const formData = {};
|
|
606
|
+
inputElements.forEach((inputElement) => {
|
|
607
|
+
formData[inputElement.name] = inputElement.value;
|
|
608
|
+
})
|
|
609
|
+
|
|
610
|
+
const validatorHelper = window.validatorHelpers[formData["check-rule-mate-validators"]][formData["check-rule-mate-validators"]];
|
|
611
|
+
|
|
612
|
+
const rules = rulesFiles.filter((rulesFile) => rulesFile.name === formData["check-rule-mate-rules"])[0].content;
|
|
613
|
+
const schema = schemasFiles.filter((schemaFile) => schemaFile.name === formName)[0].content;
|
|
614
|
+
|
|
615
|
+
const validator = createValidator(formData, {validationHelpers: validatorHelper, rules: rules, schema: schema, options: { propertiesMustMatch: false, abortEarly: false, cache: true}});
|
|
616
|
+
const result = await validator.validate();
|
|
617
|
+
console.log(formName, result);
|
|
618
|
+
|
|
619
|
+
const textAreaElement = document.querySelector('#textarea-schema-'+formName);
|
|
620
|
+
textAreaElement.value = JSON.stringify(result);
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
}, 1000);
|
|
624
|
+
`: ''}
|
|
625
|
+
`;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
module.exports = function docsPlayground(argv) {
|
|
630
|
+
const args = parseArgs(argv);
|
|
631
|
+
|
|
632
|
+
return generateDocsPlayground({
|
|
633
|
+
rulesArg: args.rules,
|
|
634
|
+
schemasArg: args.schemas,
|
|
635
|
+
errorsArg: args.errors,
|
|
636
|
+
optionsArg: args.options,
|
|
637
|
+
outArg: args.out
|
|
638
|
+
});
|
|
639
|
+
}
|