producs 1.0.1 → 3.0.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/cli.js CHANGED
@@ -1,92 +1,114 @@
1
1
  #!/usr/bin/env node
2
- const { Command } = require("commander");
3
- const fs = require("fs");
2
+ "use strict";
3
+
4
+ // ═══════════════════════════════════════════════════════════════════════════════
5
+ // ProDucs CLI — node cli.js <file.pd> [flags]
6
+ // producs <file.pd> [flags] (when installed globally)
7
+ // ═══════════════════════════════════════════════════════════════════════════════
8
+
9
+ const fs = require("fs");
4
10
  const path = require("path");
5
- const chalk = require("chalk");
6
- const { ProDucs } = require("./producs.js");
7
-
8
- const PAGES_DIR = "/usr/share/producs";
9
- const program = new Command();
10
-
11
- // ───────────────────────────────────────────────
12
- // Utility functions
13
- // ───────────────────────────────────────────────
14
- function listPages(showDesc = false) {
15
- if (!fs.existsSync(PAGES_DIR)) {
16
- console.log(chalk.red(`No pages directory found at ${PAGES_DIR}`));
17
- return;
18
- }
11
+ const { ProDucs, ProDucsError } = require("./index.js");
19
12
 
20
- const files = fs.readdirSync(PAGES_DIR).filter(f => f.endsWith(".prd"));
21
- if (files.length === 0) {
22
- console.log(chalk.yellow("No .prd pages found in"), PAGES_DIR);
23
- return;
24
- }
13
+ // ─── ANSI (minimal, used before env is up) ────────────────────────────────────
14
+ const bold = (s) => `\x1b[1m${s}\x1b[0m`;
15
+ const dim = (s) => `\x1b[2m${s}\x1b[0m`;
16
+ const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
17
+ const red = (s) => `\x1b[31m${s}\x1b[0m`;
25
18
 
26
- console.log(chalk.bold.cyan("Available ProDucs pages:\n"));
27
- for (const file of files) {
28
- const name = file.replace(/\.prd$/, "");
29
- let desc = "";
30
- if (showDesc) {
31
- try {
32
- const firstLine = fs.readFileSync(path.join(PAGES_DIR, file), "utf8").split("\n")[0];
33
- desc = " — " + firstLine.trim();
34
- } catch {}
35
- }
36
- console.log(" • " + chalk.green(name) + chalk.gray(desc));
37
- }
38
- }
19
+ const HELP = `
20
+ ${bold(cyan("ProDucs"))} Professional Documentation Shell DSL ${dim("v3.0.0")}
21
+ ${dim("".repeat(60))}
22
+ ${bold("Usage:")}
23
+ producs <file.pd> [flags]
24
+ producs --demo
25
+ producs --help
39
26
 
40
- // ───────────────────────────────────────────────
41
- // Page runner
42
- // ───────────────────────────────────────────────
43
- function runPage(name) {
44
- const filePath = path.join(PAGES_DIR, name.endsWith(".prd") ? name : name + ".prd");
45
- if (!fs.existsSync(filePath)) {
46
- console.error(chalk.red(`Page not found:`), filePath);
47
- console.log();
48
- listPages(true);
49
- process.exit(1);
50
- }
27
+ ${bold("Flags:")}
28
+ ${bold("--debug")} Verbose debug output to stderr
29
+ ${bold("--strict")} Halt on any shell command failure
30
+ ${bold("--no-color")} Disable ANSI colors
31
+ ${bold("--ast")} Dump AST as JSON and exit (no execution)
32
+ ${bold("--demo")} Run the built-in feature demo
33
+ ${bold("--help")} Show this message
34
+ ${bold("--version")} Print version
35
+
36
+ ${bold("Syntax Quick-Ref:")}
37
+ ${cyan("#$Cmd['arg1','arg2']")} inline command
38
+ ${cyan("#$Cmd1 | #$Cmd2 | #$Cmd3")} pipe chain (left→right)
39
+ ${cyan("#$Block['title']")} open a block
40
+ ${cyan("#$End_Block")} close a block
41
+ ${cyan("// comment")} line comment
42
+ ${cyan("${varname}")} variable interpolation
43
+
44
+ ${bold("Branching (replaces if/else):")}
45
+ ${cyan("#$Check_eq['expected']")} sets failed=0 (pass) or failed=1 (fail)
46
+ ${cyan("#$Branch")} runs children only when failed=0
47
+ ${cyan(" #$Print['it passed']")}
48
+ ${cyan("#$End_Branch")}
49
+ ${cyan("#$BranchElse")} runs children only when failed=1
50
+ ${cyan(" #$Print['it failed']")}
51
+ ${cyan("#$End_BranchElse")}
51
52
 
52
- const src = fs.readFileSync(filePath, "utf8");
53
- const engine = new ProDucs();
53
+ ${bold("All Check_* commands:")}
54
+ Check_eq Check_neq Check_contains Check_not_contains
55
+ Check_starts Check_ends Check_matches Check_empty
56
+ Check_nonempty Check_gt Check_gte Check_lt
57
+ Check_lte Check_num_eq Check_num_neq Check_between
58
+ Check_failed Check_passed Check_var_eq Check_var_neq
59
+ Check_failed_error Check_passed_error
54
60
 
55
- console.log(chalk.yellowBright(`\n── Running page: ${name} ──\n`));
56
- try {
57
- const ast = engine.astify(src);
58
- engine.execute(ast);
59
- console.log(chalk.greenBright("\n✔ Page completed successfully.\n"));
60
- } catch (err) {
61
- console.error(chalk.red.bold("\n✖ Execution error:"), err.message, "\n");
61
+ ${bold("File extension:")} .pd (ProDucs script)
62
+ ${dim("─".repeat(60))}
63
+ `;
64
+
65
+ const DEMO = require("fs").readFileSync(
66
+ require("path").join(__dirname, "examples", "demo.pd"), "utf8"
67
+ );
68
+ // ─── Parse argv ───────────────────────────────────────────────────────────────
69
+ const argv = process.argv.slice(2);
70
+ const flags = new Set(argv.filter(a => a.startsWith("--")));
71
+ const files = argv.filter(a => !a.startsWith("--"));
72
+
73
+ if (flags.has("--version")) { process.stdout.write("ProDucs v3.0.0\n"); process.exit(0); }
74
+ if (flags.has("--help") || process.argv.length === 2) { process.stdout.write(HELP); process.exit(0); }
75
+
76
+ const opts = {
77
+ debug: flags.has("--debug"),
78
+ strict: flags.has("--strict"),
79
+ noColor: flags.has("--no-color"),
80
+ };
81
+
82
+ const prod = new ProDucs(opts);
83
+
84
+ // ─── Demo ─────────────────────────────────────────────────────────────────────
85
+ if (flags.has("--demo")) {
86
+ try { prod.run(DEMO); }
87
+ catch (e) {
88
+ process.stderr.write(red(`\n${e.name||"Error"}: ${e.message}\n`));
89
+ if (opts.debug && e.stack) process.stderr.write(dim(e.stack)+"\n");
62
90
  process.exit(1);
63
91
  }
92
+ process.exit(0);
64
93
  }
65
94
 
66
- // ───────────────────────────────────────────────
67
- // CLI setup with commander
68
- // ───────────────────────────────────────────────
69
- program
70
- .name("producs")
71
- .description("Interactive documentation and tutorial runner")
72
- .version("1.0.0");
73
-
74
- program
75
- .command("list")
76
- .description("List all available ProDucs pages")
77
- .option("-d, --describe", "Show first-line descriptions for each page")
78
- .action((options) => listPages(options.describe));
79
-
80
- program
81
- .command("run <page>")
82
- .description("Run a specific ProDucs documentation page")
83
- .action(runPage);
84
-
85
- program
86
- .argument("[page]", "Run a page directly (shortcut for 'run <page>')")
87
- .action((page) => {
88
- if (page) runPage(page);
89
- else listPages(true);
90
- });
91
-
92
- program.parse(process.argv);
95
+ // ─── File execution ───────────────────────────────────────────────────────────
96
+ const filePath = path.resolve(files[0]);
97
+ let src;
98
+ try { src = fs.readFileSync(filePath, "utf8"); }
99
+ catch (e) {
100
+ process.stderr.write(red(`Cannot read: ${filePath}\n`));
101
+ process.exit(1);
102
+ }
103
+
104
+ if (flags.has("--ast")) {
105
+ console.log(JSON.stringify(prod.astify(src), null, 2));
106
+ process.exit(0);
107
+ }
108
+
109
+ try { prod.run(src); }
110
+ catch (e) {
111
+ process.stderr.write(red(`\n${e.name||"Error"}: ${e.message}\n`));
112
+ if (opts.debug && e.stack) process.stderr.write(dim(e.stack)+"\n");
113
+ process.exit(1);
114
+ }
@@ -0,0 +1,126 @@
1
+ // checks.pd — showcase all Check_* commands
2
+
3
+ #$Header['ProDucs Check Commands Demo']
4
+
5
+ // ── String Checks ─────────────────────────────────────────────────
6
+ #$Section['String Checks on VALUE']
7
+
8
+ #$Set['val','Hello World']
9
+
10
+ #$Set['val','${val}'] | #$Check_eq['Hello World']
11
+ #$Branch
12
+ #$Color['green','Check_eq: PASS']
13
+ #$End_Branch
14
+
15
+ #$Set['val','${val}'] | #$Check_contains['World']
16
+ #$Branch
17
+ #$Color['green','Check_contains: PASS']
18
+ #$End_Branch
19
+
20
+ #$Set['val','${val}'] | #$Check_starts['Hello']
21
+ #$Branch
22
+ #$Color['green','Check_starts: PASS']
23
+ #$End_Branch
24
+
25
+ #$Set['val','${val}'] | #$Check_ends['World']
26
+ #$Branch
27
+ #$Color['green','Check_ends: PASS']
28
+ #$End_Branch
29
+
30
+ #$Set['val','${val}'] | #$Check_matches['^Hello']
31
+ #$Branch
32
+ #$Color['green','Check_matches: PASS']
33
+ #$End_Branch
34
+
35
+ #$Set['empty','']
36
+ #$Set['empty','${empty}'] | #$Check_empty
37
+ #$Branch
38
+ #$Color['green','Check_empty: PASS']
39
+ #$End_Branch
40
+
41
+ #$Set['val','${val}'] | #$Check_nonempty
42
+ #$Branch
43
+ #$Color['green','Check_nonempty: PASS']
44
+ #$End_Branch
45
+
46
+ #$End_Section
47
+
48
+ // ── Numeric Checks ────────────────────────────────────────────────
49
+ #$Section['Numeric Checks']
50
+
51
+ #$Set['score','85']
52
+
53
+ #$Check_gt['score','80']
54
+ #$Branch
55
+ #$Color['green','Check_gt score > 80: PASS']
56
+ #$End_Branch
57
+
58
+ #$Check_gte['score','85']
59
+ #$Branch
60
+ #$Color['green','Check_gte score >= 85: PASS']
61
+ #$End_Branch
62
+
63
+ #$Check_lt['score','90']
64
+ #$Branch
65
+ #$Color['green','Check_lt score < 90: PASS']
66
+ #$End_Branch
67
+
68
+ #$Check_lte['score','85']
69
+ #$Branch
70
+ #$Color['green','Check_lte score <= 85: PASS']
71
+ #$End_Branch
72
+
73
+ #$Check_num_eq['score','85']
74
+ #$Branch
75
+ #$Color['green','Check_num_eq score == 85: PASS']
76
+ #$End_Branch
77
+
78
+ #$Check_between['score','80','90']
79
+ #$Branch
80
+ #$Color['green','Check_between score in [80,90]: PASS']
81
+ #$End_Branch
82
+
83
+ #$End_Section
84
+
85
+ // ── Variable Comparison ───────────────────────────────────────────
86
+ #$Section['Variable Comparison']
87
+
88
+ #$Set['a','hello']
89
+ #$Set['b','hello']
90
+ #$Set['c','world']
91
+
92
+ #$Check_var_eq['a','b']
93
+ #$Branch
94
+ #$Color['green','Check_var_eq a===b: PASS']
95
+ #$End_Branch
96
+
97
+ #$Check_var_neq['a','c']
98
+ #$Branch
99
+ #$Color['green','Check_var_neq a!==c: PASS']
100
+ #$End_Branch
101
+
102
+ #$End_Section
103
+
104
+ // ── Result / State Checks ─────────────────────────────────────────
105
+ #$Section['Result State Checks']
106
+
107
+ // Force a fail state
108
+ #$Set['x','5'] | #$Check_gt['10']
109
+
110
+ #$Check_failed
111
+ #$Branch
112
+ #$Color['green','Check_failed: PASS (previous check did fail)']
113
+ #$End_Branch
114
+
115
+ // Now pass
116
+ #$Set['x','15'] | #$Check_gt['10']
117
+
118
+ #$Check_passed
119
+ #$Branch
120
+ #$Color['green','Check_passed: PASS (previous check passed)']
121
+ #$End_Branch
122
+
123
+ #$End_Section
124
+
125
+ #$HR
126
+ #$Status['All checks demonstrated']
@@ -0,0 +1,113 @@
1
+ #$Header['ProDucs v3.0 Demo']
2
+
3
+ // ── Variables & Math ──────────────────────────────────────────────
4
+ #$Section['Variables & Math']
5
+ #$Set['x','40']
6
+ #$Set['y','2']
7
+ #$Math['x * y'] | #$Set['product']
8
+ #$Print['${x} * ${y} = ${product}']
9
+ #$Inc['x','5']
10
+ #$Print['x after Inc: ${x}']
11
+ #$End_Section
12
+
13
+ // ── Pipe chain ────────────────────────────────────────────────────
14
+ #$Section['Pipe Chain']
15
+ #$Set['msg',' hello world ']
16
+ #$Set['msg','${msg}'] | #$Trim | #$Upper | #$Set['msg']
17
+ #$Print['Processed: ${msg}']
18
+ #$Len | #$Set['len']
19
+ #$Print['Length: ${len}']
20
+ #$End_Section
21
+
22
+ // ── Branch on string check ────────────────────────────────────────
23
+ #$Section['Branch: String Check']
24
+ #$Set['role','admin']
25
+ #$Set['role','${role}'] | #$Check_eq['admin']
26
+ #$Branch
27
+ #$Color['green','✔ Role is admin']
28
+ #$End_Branch
29
+ #$BranchElse
30
+ #$Color['red','✖ Role is not admin']
31
+ #$End_BranchElse
32
+ #$End_Section
33
+
34
+ // ── Branch on numeric check ───────────────────────────────────────
35
+ #$Section['Branch: Numeric Check']
36
+ #$Set['score','87']
37
+ #$Check_gte['score','80']
38
+ #$Branch
39
+ #$Color['green','✔ Score ${score} >= 80: Pass']
40
+ #$End_Branch
41
+ #$BranchElse
42
+ #$Color['red','✖ Score ${score} < 80: Fail']
43
+ #$End_BranchElse
44
+
45
+ #$Check_between['score','90','100']
46
+ #$Branch
47
+ #$Print['Score is A-grade']
48
+ #$End_Branch
49
+ #$BranchElse
50
+ #$Print['Score is not A-grade (${score} not in 90-100)']
51
+ #$End_BranchElse
52
+ #$End_Section
53
+
54
+ // ── Repeat ────────────────────────────────────────────────────────
55
+ #$Section['Repeat']
56
+ #$Repeat['3']
57
+ #$Print['Iteration ${_i}']
58
+ #$End_Repeat
59
+ #$End_Section
60
+
61
+ // ── ForEach ───────────────────────────────────────────────────────
62
+ #$Section['ForEach']
63
+ #$ForEach['lang','Nova','C','Zig']
64
+ #$Print['Language: ${lang}']
65
+ #$End_ForEach
66
+ #$End_Section
67
+
68
+ // ── Goto & Label ─────────────────────────────────────────────────
69
+ #$Section['Goto & Label']
70
+ #$Set['skip','yes']
71
+ #$Set['skip','${skip}'] | #$Check_eq['yes']
72
+ #$Branch
73
+ #$Goto['after_block']
74
+ #$End_Branch
75
+ #$Print['THIS MUST NOT APPEAR']
76
+ #$Label['after_block']
77
+ #$Print['✔ Jumped here via Goto']
78
+ #$End_Section
79
+
80
+ // ── Try / Catch ───────────────────────────────────────────────────
81
+ #$Section['Try / Catch']
82
+ #$Try
83
+ #$Assert['1===2','intentional failure']
84
+ #$Catch
85
+ #$Print['Caught: ${_error}']
86
+ #$End_Catch
87
+ #$End_Try
88
+ #$End_Section
89
+
90
+ // ── Wait ──────────────────────────────────────────────────────────
91
+ #$Section['Wait']
92
+ #$Spinner['Waiting 300ms...']
93
+ #$Wait['300']
94
+ #$Color['green','Done.']
95
+ #$End_Section
96
+
97
+ // ── Table ─────────────────────────────────────────────────────────
98
+ #$Section['Table']
99
+ #$Table
100
+ #$Row['Command','Category','Description']
101
+ #$Row['Check_gt','Numeric','Greater-than check']
102
+ #$Row['Check_between','Numeric','Range check']
103
+ #$Row['Branch','Control','Run if check passed']
104
+ #$Row['BranchElse','Control','Run if check failed']
105
+ #$Row['Goto','Control','Jump to label']
106
+ #$Row['Wait','Timing','Blocking sleep (ms)']
107
+ #$Row['Shell_capture','Shell','Capture stdout']
108
+ #$End_Table
109
+ #$End_Section
110
+
111
+ #$HR['=']
112
+ #$Status['Demo complete']
113
+ #$Dump_log
@@ -0,0 +1,43 @@
1
+ // deploy.pd — deployment checklist example
2
+
3
+ #$Header['Deploy Checklist']
4
+
5
+ #$Section['Pre-flight Checks']
6
+ #$Require_cmd['git']
7
+ #$Require_cmd['node']
8
+ #$Color['green','✔ Required commands found']
9
+
10
+ #$Timestamp['iso'] | #$Set['deploy_time']
11
+ #$Print['Deploy started: ${deploy_time}']
12
+ #$End_Section
13
+
14
+ #$Section['Branch Check']
15
+ #$Shell_capture['git branch --show-current'] | #$Set['branch']
16
+ #$Print['Current branch: ${branch}']
17
+
18
+ #$Set['branch','${branch}'] | #$Check_eq['main']
19
+ #$Branch
20
+ #$Color['green','✔ On main branch']
21
+ #$End_Branch
22
+ #$BranchElse
23
+ #$Warn['Not on main branch — deploying ${branch}']
24
+ #$Confirm['Continue anyway?','n']
25
+ #$Check_passed_error['Aborted by user']
26
+ #$End_BranchElse
27
+ #$End_Section
28
+
29
+ #$Section['Build Steps']
30
+ #$Step['Install dependencies']
31
+ #$Shell_run['npm ci']
32
+ #$Status['npm ci']
33
+ #$End_Step
34
+
35
+ #$Step['Run tests']
36
+ #$Shell_run['npm test']
37
+ #$Status['npm test']
38
+ #$End_Step
39
+ #$End_Section
40
+
41
+ #$HR['═']
42
+ #$Status['Deploy complete']
43
+ #$Dump_log