precedent 1.0.14 → 1.0.16

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/.babelrc ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "presets": [
3
+ "@babel/preset-env"
4
+ ]
5
+ }
@@ -0,0 +1,50 @@
1
+ # Contributing to Retold
2
+
3
+ We welcome contributions to Retold and its modules. This guide covers the expectations and process for contributing.
4
+
5
+ ## Code of Conduct
6
+
7
+ The Retold community values **empathy**, **equity**, **kindness**, and **thoughtfulness**. We expect all participants to treat each other with respect, assume good intent, and engage constructively. These values apply to all interactions: pull requests, issues, discussions, and code review.
8
+
9
+ ## How to Contribute
10
+
11
+ ### Pull Requests
12
+
13
+ Pull requests are the preferred method for contributing changes. To submit one:
14
+
15
+ 1. Fork the module repository you want to change
16
+ 2. Create a branch for your work
17
+ 3. Make your changes, following the code style of the module you are editing
18
+ 4. Ensure your changes have test coverage (see below)
19
+ 5. Open a pull request against the module's main branch
20
+
21
+ **Submitting a pull request does not guarantee it will be accepted.** Maintainers review contributions for fit, quality, and alignment with the project's direction. A PR may be declined, or you may be asked to revise it. This is normal and not a reflection on the quality of your effort.
22
+
23
+ ### Reporting Issues
24
+
25
+ If you find a bug or have a feature suggestion, open an issue on the relevant module's repository. Include enough detail to reproduce the problem or understand the proposal.
26
+
27
+ ## Test Coverage
28
+
29
+ Every commit must include test coverage for the changes it introduces. Retold modules use Mocha in TDD style. Before submitting:
30
+
31
+ - **Write tests** for any new functionality or bug fixes
32
+ - **Run the existing test suite** with `npm test` and confirm all tests pass
33
+ - **Check coverage** with `npm run coverage` if the module supports it
34
+
35
+ Pull requests that break existing tests or lack coverage for new code will not be merged.
36
+
37
+ ## Code Style
38
+
39
+ Follow the conventions of the module you are working in. The general Retold style is:
40
+
41
+ - **Tabs** for indentation, never spaces
42
+ - **Plain JavaScript** only (no TypeScript)
43
+ - **Allman-style braces** (opening brace on its own line)
44
+ - **Variable naming:** `pVariable` for parameters, `tmpVariable` for temporaries, `libSomething` for imports
45
+
46
+ When in doubt, match what the surrounding code does.
47
+
48
+ ## Repository Structure
49
+
50
+ Each module is its own git repository. The [retold](https://github.com/stevenvelozo/retold) repository tracks module organization but does not contain module source code. Direct your pull request to the specific module repository where your change belongs.
package/README.md CHANGED
@@ -1,76 +1,149 @@
1
1
  # Precedent
2
- Precedent meta-templating engine, for when you want templates ... for templates.
3
2
 
4
- [![Code Climate](https://codeclimate.com/github/stevenvelozo/precedent/badges/gpa.svg)](https://codeclimate.com/github/stevenvelozo/precedent) [![Build Status](https://travis-ci.org/stevenvelozo/precedent.svg?branch=master)](https://travis-ci.org/stevenvelozo/precedent) [![Dependency Status](https://david-dm.org/stevenvelozo/precedent.svg)](https://david-dm.org/stevenvelozo/precedent) [![devDependency Status](https://david-dm.org/stevenvelozo/precedent/dev-status.svg)](https://david-dm.org/stevenvelozo/precedent#info=devDependencies)
3
+ A meta-templating engine for processing text streams with pattern-based template expressions. Define start/end pattern markers with string or function parsers, and Precedent handles nested pattern resolution automatically.
5
4
 
6
- ## Template Patterns
5
+ [![Coverage Status](https://coveralls.io/repos/github/stevenvelozo/precedent/badge.svg?branch=master)](https://coveralls.io/github/stevenvelozo/precedent?branch=master)
6
+ [![Build Status](https://github.com/stevenvelozo/precedent/workflows/Precedent/badge.svg)](https://github.com/stevenvelozo/precedent/actions)
7
+ [![npm version](https://badge.fury.io/js/precedent.svg)](https://badge.fury.io/js/precedent)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
9
 
8
- Precedent works on the concept of "Template Patterns". These are regions of text that are replaced by their template function. Because patterns are defined in a tree data structure, nested patterns (such as `<%`, `<%=`, `<$$` and `<`) properly get parsed in the same process run.
9
-
10
- So, for instance, you could create a pattern like so:
10
+ ---
11
11
 
12
+ ## Features
12
13
 
13
- ```js
14
- // Load the precedent library
15
- var libPrecedent = require('../source/Precedent.js').new();
14
+ - **Pattern-Based Parsing** - Define start/end markers to identify template regions in text
15
+ - **String or Function Parsers** - Replace patterns with static strings or dynamic function output
16
+ - **Nested Pattern Support** - Overlapping pattern prefixes (e.g. `<%`, `<%=`, `<$$`) resolve correctly in a single pass
17
+ - **Word Tree Architecture** - Patterns are stored in a tree structure for efficient matching
18
+ - **Data Passing** - Pass a data object through to parser functions for context-aware rendering
19
+ - **Browser Compatible** - Works in both Node.js and browser environments
20
+ - **Zero Dependencies** - No external runtime dependencies
16
21
 
17
- // Add the pattern
18
- libPrecedent.addPattern('{Name', '}', 'David Bowie');
22
+ ## Installation
19
23
 
20
- // Parse a string with the pattern
21
- console.log(libPrecedent.parseString('This is just a short message for {Name}.');
22
- // Anything inbetween the start and end is ignored in this case, since it is a string substitution.
23
- console.log(libPrecedent.parseString('This is just a short message for {Name THIS TEXT IS IGNORED}. We hope to ignore the previous text.');
24
+ ```bash
25
+ npm install precedent
24
26
  ```
25
27
 
26
- This would output the following to the console:
28
+ ## Quick Start
29
+
30
+ ```javascript
31
+ const Precedent = require('precedent');
32
+
33
+ const precedent = new Precedent();
27
34
 
35
+ // Add a simple string substitution pattern
36
+ precedent.addPattern('{Name', '}', 'David Bowie');
37
+
38
+ // Parse a string containing the pattern
39
+ precedent.parseString('Hello, {Name}!');
40
+ // => "Hello, David Bowie!"
28
41
  ```
29
- This is just a short message for David Bowie.
30
- This is just a short message for David Bowie. We hope to ignore the previous text.
42
+
43
+ ## Usage
44
+
45
+ ### String Substitution
46
+
47
+ Replace patterns with a fixed string value. Content between the start and end markers is ignored:
48
+
49
+ ```javascript
50
+ const precedent = new Precedent();
51
+
52
+ precedent.addPattern('{Name', '}', 'David Bowie');
53
+
54
+ precedent.parseString('A message for {Name}.');
55
+ // => "A message for David Bowie."
56
+
57
+ // Content between markers is ignored for string substitutions
58
+ precedent.parseString('A message for {Name IGNORED TEXT}.');
59
+ // => "A message for David Bowie."
31
60
  ```
32
61
 
33
- ---
62
+ ### Function-Based Parsing
63
+
64
+ Pass a function as the parser to dynamically process the content between markers:
65
+
66
+ ```javascript
67
+ const precedent = new Precedent();
34
68
 
35
- ## precedent.addPattern(patternStart, patternEnd, parser)
69
+ precedent.addPattern('{Length', '}', (pString) => { return pString.length; });
36
70
 
37
- Add a pattern to the string processor.
71
+ precedent.parseString('The length is {Length some text}.');
72
+ // => "The length is some text." (length of " some text")
73
+ ```
74
+
75
+ ### Passing Data to Parsers
76
+
77
+ A data object can be passed as the second argument to `parseString`, which is then available to parser functions:
38
78
 
39
79
  ```javascript
40
- // Pass in a string
41
- libPrecedent.addPattern('{Name', '}', 'David Bowie');
80
+ const precedent = new Precedent();
42
81
 
43
- // Or a function
44
- libPrecedent.addPattern('{Name', '}', (pString)=>{return pString.length;});
82
+ precedent.addPattern('<%=', '%>', (pContent, pData) =>
83
+ {
84
+ return pData[pContent.trim()] || '';
85
+ });
86
+
87
+ precedent.parseString('Hello, <%= username %>!', { username: 'Steven' });
88
+ // => "Hello, Steven!"
45
89
  ```
46
90
 
47
- Each time a pattern is matched, anything between the `patternStart` and `patternEnd` will be passed into the parse function.
91
+ ## API
48
92
 
49
- #### patternStart
50
- Type: `String`
93
+ ### `addPattern(patternStart, patternEnd, parser)`
51
94
 
52
- The beginning portion of a pattern.
95
+ Add a pattern to the parse tree.
53
96
 
54
- #### patternEnd
55
- Type: `String`
97
+ | Parameter | Type | Description |
98
+ |-----------|------|-------------|
99
+ | `patternStart` | `String` | The opening marker for the pattern |
100
+ | `patternEnd` | `String` | The closing marker for the pattern |
101
+ | `parser` | `String` or `Function` | Replacement string, or function receiving `(content, data)` |
56
102
 
57
- The ending portion of a pattern.
103
+ Returns `true` if the pattern was added successfully.
58
104
 
59
- ##### parser
60
- Type: `String` or `Function`
61
- Default: Echo content between the pattern start and end.
105
+ ### `parseString(contentString, data)`
62
106
 
63
- ---
107
+ Parse a string against all registered patterns.
64
108
 
65
- ## precedent.parseString(contentString)
109
+ | Parameter | Type | Description |
110
+ |-----------|------|-------------|
111
+ | `contentString` | `String` | The text to parse |
112
+ | `data` | `Object` | Optional data object passed to parser functions |
66
113
 
67
- Parse a string with the processor.
114
+ Returns the parsed string.
68
115
 
69
- ```javascript
70
- libPrecedent.parseString('This is just a short message for {Name}.'
116
+ ## Part of the Retold Framework
117
+
118
+ Precedent is used throughout the Fable ecosystem for template processing:
119
+
120
+ - [fable](https://github.com/stevenvelozo/fable) - Application services framework
121
+ - [pict](https://github.com/stevenvelozo/pict) - UI framework
122
+ - [pict-template](https://github.com/stevenvelozo/pict-template) - Template engine built on Precedent
123
+
124
+ ## Testing
125
+
126
+ Run the test suite:
127
+
128
+ ```bash
129
+ npm test
130
+ ```
131
+
132
+ Run with coverage:
133
+
134
+ ```bash
135
+ npm run coverage
71
136
  ```
72
137
 
73
- #### contentString
74
- Type: `String`
138
+ ## Related Packages
139
+
140
+ - [fable](https://github.com/stevenvelozo/fable) - Application services framework
141
+ - [pict-template](https://github.com/stevenvelozo/pict-template) - Template engine
142
+
143
+ ## License
144
+
145
+ MIT
146
+
147
+ ## Contributing
75
148
 
76
- The string of content to parseg
149
+ Pull requests are welcome. For details on our code of conduct, contribution process, and testing requirements, see the [Retold Contributing Guide](https://github.com/stevenvelozo/retold/blob/main/docs/contributing.md).
package/docs/.nojekyll ADDED
File without changes
package/docs/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # Precedent
2
+
3
+ A meta-templating engine that parses text strings and replaces delimited regions with custom output. Register start/end delimiter pairs, attach a handler function or replacement string, then parse any text through the engine.
4
+
5
+ Precedent is the foundation beneath Pict's `{~...~}` template expression system and Fable Settings' `${...}` environment variable substitution. It has zero runtime dependencies and works in both Node.js and the browser.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install precedent
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```javascript
16
+ const libPrecedent = require('precedent');
17
+ let processor = new libPrecedent();
18
+
19
+ // Register a pattern: replace anything between << and >> with "REDACTED"
20
+ processor.addPattern('<<', '>>', 'REDACTED');
21
+
22
+ let result = processor.parseString('The code is <<SECRET123>> end.');
23
+ // => "The code is REDACTED end."
24
+ ```
25
+
26
+ ## Core Concepts
27
+
28
+ ### 1. Patterns
29
+
30
+ A pattern is a pair of delimiter strings (start and end) plus a handler. When the parser encounters the start delimiter in a string, it captures everything until the end delimiter, then calls the handler.
31
+
32
+ ```javascript
33
+ processor.addPattern(startDelimiter, endDelimiter, handler);
34
+ ```
35
+
36
+ ### 2. Handlers
37
+
38
+ The handler can be:
39
+
40
+ | Type | Behavior |
41
+ |------|----------|
42
+ | **string** | The matched region is replaced with this string |
43
+ | **function** | Called with `(content, data)` — content between delimiters and the data argument from `parseString()` |
44
+ | **omitted** | The content between delimiters is passed through unchanged |
45
+
46
+ ### 3. Data Passing
47
+
48
+ The second argument to `parseString()` is passed to every handler function as its second parameter:
49
+
50
+ ```javascript
51
+ processor.addPattern('{', '}',
52
+ (pContent, pData) =>
53
+ {
54
+ return pData[pContent];
55
+ });
56
+
57
+ let result = processor.parseString('Hello, {name}!', { name: 'Alice' });
58
+ // => "Hello, Alice!"
59
+ ```
60
+
61
+ ### 4. Pattern Precedence
62
+
63
+ Patterns are stored in a character-by-character word tree (directed graph). When multiple patterns share a prefix, the longest matching start delimiter wins:
64
+
65
+ ```javascript
66
+ processor.addPattern('<', '>', 'SHORT');
67
+ processor.addPattern('<<', '>', 'MEDIUM');
68
+ processor.addPattern('<<LONG', '>', 'LONG');
69
+
70
+ processor.parseString('<x>'); // => "SHORT"
71
+ processor.parseString('<<x>'); // => "MEDIUM"
72
+ processor.parseString('<<LONGx>'); // => "LONG"
73
+ ```
74
+
75
+ If a longer match starts but fails to complete, the parser falls back to the shorter match.
76
+
77
+ ## How It Works
78
+
79
+ ```
80
+ addPattern('${', '}', fn)
81
+
82
+
83
+ ┌──────────────────────────────────┐
84
+ │ WordTree │
85
+ │ │
86
+ │ Builds a character-by-character │
87
+ │ tree from start delimiters. │
88
+ │ End delimiters are stored as │
89
+ │ subtrees on the terminal node. │
90
+ │ │
91
+ │ $ ──▶ { ──▶ PatternEnd ──▶ } │
92
+ │ └─ Parse() │
93
+ └──────────────────────────────────┘
94
+
95
+
96
+ ┌──────────────────────────────────┐
97
+ │ StringParser │
98
+ │ │
99
+ │ Scans character by character: │
100
+ │ 1. Match start delimiter chars │
101
+ │ 2. Buffer content │
102
+ │ 3. Match end delimiter chars │
103
+ │ 4. Call Parse(content, data) │
104
+ │ 5. Replace matched region │
105
+ └──────────────────────────────────┘
106
+ ```
107
+
108
+ The parser is stateful and stream-oriented — it processes one character at a time, tracking whether it is in raw mode, matching a start delimiter, buffering content, or matching an end delimiter.
109
+
110
+ ## Architecture
111
+
112
+ Precedent consists of three classes:
113
+
114
+ | Class | Responsibility |
115
+ |-------|---------------|
116
+ | **Precedent** | Public facade — exposes `addPattern()` and `parseString()` |
117
+ | **WordTree** | Stores delimiter patterns in a character-based tree structure |
118
+ | **StringParser** | Character-by-character parser that traverses the tree and invokes handlers |
119
+
120
+ ## Browser Usage
121
+
122
+ The browser shim (`Precedent-Browser-Shim.js`) assigns the constructor to `window.Precedent`:
123
+
124
+ ```html
125
+ <script src="precedent.min.js"></script>
126
+ <script>
127
+ var processor = new Precedent();
128
+ processor.addPattern('{{', '}}', function(content) { return content.toUpperCase(); });
129
+ document.body.innerHTML = processor.parseString('Hello {{world}}');
130
+ // => "Hello WORLD"
131
+ </script>
132
+ ```
133
+
134
+ Build with Quackage: `npx quack build`
135
+
136
+ ## Where Precedent Is Used
137
+
138
+ | Consumer | Pattern | Purpose |
139
+ |----------|---------|---------|
140
+ | **Fable Settings** | `${VAR\|default}` | Environment variable substitution in configuration |
141
+ | **Pict** | `{~Prefix:content~}` | 40+ template expression types for data, logic, rendering |
142
+ | **Pict Template** | Custom patterns | Base class for all Pict template expressions |
@@ -0,0 +1,19 @@
1
+ - Getting Started
2
+
3
+ - [Overview](README.md)
4
+
5
+ - Reference
6
+
7
+ - [API Reference](api.md)
8
+
9
+ - Examples
10
+
11
+ - [Usage Patterns](examples.md)
12
+
13
+ - Retold Ecosystem
14
+
15
+ - [Pict](/pict/pict/)
16
+ - [Pict Template](/pict/pict-template/)
17
+ - [Fable](/fable/fable/)
18
+ - [Fable Settings](/fable/fable-settings/)
19
+ - [Indoctrinate](/utility/indoctrinate/)
@@ -0,0 +1,5 @@
1
+ # Precedent
2
+
3
+ - [Overview](README.md)
4
+ - [API Reference](api.md)
5
+ - [GitHub](https://github.com/stevenvelozo/precedent)
package/docs/api.md ADDED
@@ -0,0 +1,209 @@
1
+ # API Reference
2
+
3
+ ## Class: Precedent
4
+
5
+ The main public interface. Creates a WordTree for pattern storage and a StringParser for execution.
6
+
7
+ ### Constructor
8
+
9
+ ```javascript
10
+ const libPrecedent = require('precedent');
11
+ let processor = new libPrecedent();
12
+ ```
13
+
14
+ No parameters. The constructor initializes an empty parse tree.
15
+
16
+ ---
17
+
18
+ ## Properties
19
+
20
+ ### ParseTree
21
+
22
+ The root node of the internal word tree. This is the directed graph that stores all registered patterns as character-by-character paths.
23
+
24
+ **Type:** `object`
25
+
26
+ Inspect it to see the current tree structure:
27
+
28
+ ```javascript
29
+ processor.addPattern('${', '}', myHandler);
30
+ console.log(JSON.stringify(processor.ParseTree, null, 2));
31
+ ```
32
+
33
+ ### WordTree
34
+
35
+ The WordTree instance that manages pattern storage.
36
+
37
+ **Type:** `WordTree`
38
+
39
+ ### StringParser
40
+
41
+ The StringParser instance that executes pattern matching.
42
+
43
+ **Type:** `StringParser`
44
+
45
+ ---
46
+
47
+ ## Methods
48
+
49
+ ### addPattern(pPatternStart, pPatternEnd, pParser)
50
+
51
+ Register a delimiter pair and a handler with the parse tree.
52
+
53
+ | Parameter | Type | Required | Description |
54
+ |-----------|------|----------|-------------|
55
+ | `pPatternStart` | string | Yes | The opening delimiter (e.g. `'${'`, `'<%'`, `'<'`) |
56
+ | `pPatternEnd` | string | No | The closing delimiter (e.g. `'}'`, `'%>'`, `'>'`). If omitted, defaults to `pPatternStart` |
57
+ | `pParser` | string \| function | No | The handler (see below) |
58
+
59
+ **Returns:** `boolean` — `true` if the pattern was added, `false` if the start or end delimiter was empty.
60
+
61
+ #### Handler Types
62
+
63
+ **String handler** — Replace the matched region with this literal string:
64
+
65
+ ```javascript
66
+ processor.addPattern('<%', '%>', 'REPLACED');
67
+ processor.parseString('A <%stuff%> B');
68
+ // => "A REPLACED B"
69
+ ```
70
+
71
+ **Function handler** — Called with two arguments:
72
+
73
+ | Argument | Description |
74
+ |----------|-------------|
75
+ | `pContent` | The text between the start and end delimiters (delimiters stripped) |
76
+ | `pData` | The second argument passed to `parseString()` |
77
+
78
+ ```javascript
79
+ processor.addPattern('<%#', '%>',
80
+ (pContent, pData) =>
81
+ {
82
+ return pContent.length;
83
+ });
84
+
85
+ processor.parseString('Count: <%#ABCDE%>');
86
+ // => "Count: 5"
87
+ ```
88
+
89
+ **No handler** — The content between delimiters is passed through (delimiters are stripped):
90
+
91
+ ```javascript
92
+ processor.addPattern('$');
93
+ processor.parseString('A $comment$ B');
94
+ // => "A comment B"
95
+ ```
96
+
97
+ #### Self-Closing Patterns
98
+
99
+ When only `pPatternStart` is provided (no end delimiter), the start string is used as both the opening and closing delimiter:
100
+
101
+ ```javascript
102
+ processor.addPattern('$');
103
+ // Equivalent to: processor.addPattern('$', '$')
104
+
105
+ processor.parseString('Hello $World$ Goodbye');
106
+ // => "Hello World Goodbye"
107
+ ```
108
+
109
+ ---
110
+
111
+ ### parseString(pString, pData)
112
+
113
+ Parse a string against all registered patterns, replacing matched regions with handler output.
114
+
115
+ | Parameter | Type | Required | Description |
116
+ |-----------|------|----------|-------------|
117
+ | `pString` | string | Yes | The text to parse |
118
+ | `pData` | any | No | Passed as the second argument to every function handler |
119
+
120
+ **Returns:** `string` — The processed string with all matched patterns replaced.
121
+
122
+ ```javascript
123
+ processor.addPattern('{', '}',
124
+ (pContent, pData) =>
125
+ {
126
+ return pData[pContent] || pContent;
127
+ });
128
+
129
+ let result = processor.parseString(
130
+ 'Hello {name}, welcome to {place}.',
131
+ { name: 'Alice', place: 'Wonderland' }
132
+ );
133
+ // => "Hello Alice, welcome to Wonderland."
134
+ ```
135
+
136
+ If no patterns match, the input string is returned unchanged.
137
+
138
+ ---
139
+
140
+ ## Class: WordTree
141
+
142
+ Builds and maintains the internal directed graph for pattern matching. You rarely interact with this directly — `Precedent.addPattern()` delegates to it.
143
+
144
+ ### addPattern(pPatternStart, pPatternEnd, fParser)
145
+
146
+ Adds a pattern to the tree. Each character of the start delimiter becomes a node. The end delimiter is stored as a subtree (`PatternEnd`) on the terminal start node. The handler function is stored on the terminal end node as `Parse`.
147
+
148
+ **Returns:** `boolean`
149
+
150
+ ### Tree Structure
151
+
152
+ For `addPattern('${', '}', fn)`, the tree looks like:
153
+
154
+ ```
155
+ ParseTree
156
+ └── '$'
157
+ └── '{'
158
+ └── PatternEnd
159
+ └── '}'
160
+ ├── PatternStartString: '${'
161
+ ├── PatternEndString: '}'
162
+ └── Parse: fn
163
+ ```
164
+
165
+ Multiple patterns sharing a prefix character (e.g. `<`, `<<`, `<<LONG`) branch naturally within the tree, enabling longest-match-first behavior.
166
+
167
+ ---
168
+
169
+ ## Class: StringParser
170
+
171
+ Character-by-character parser that traverses the WordTree and executes handler functions. You rarely interact with this directly — `Precedent.parseString()` delegates to it.
172
+
173
+ ### parseString(pString, pParseTree, pData)
174
+
175
+ Scans the input string one character at a time, maintaining a state object that tracks:
176
+
177
+ | State Property | Purpose |
178
+ |---------------|---------|
179
+ | `Output` | Accumulated final output |
180
+ | `OutputBuffer` | Characters being buffered during a potential match |
181
+ | `Pattern` | Current node in the word tree |
182
+ | `PatternMatch` | Whether we are currently in a pattern match |
183
+ | `StartPatternMatchComplete` | Whether the full start delimiter has been matched |
184
+ | `EndPatternMatchBegan` | Whether end delimiter matching has started |
185
+
186
+ ### Parsing Flow
187
+
188
+ 1. **Raw mode** — Characters are buffered directly to output
189
+ 2. **Start match** — A character matches the tree root; parser begins traversing the tree
190
+ 3. **Content capture** — Start delimiter fully matched; characters are buffered as content
191
+ 4. **End match** — End delimiter characters begin matching the PatternEnd subtree
192
+ 5. **Execution** — End delimiter fully matched; handler is called with `(content, data)`
193
+ 6. **Replacement** — Handler output replaces the entire matched region (delimiters + content)
194
+ 7. **Reset** — Parser returns to raw mode
195
+
196
+ If a partial start match fails (the next character does not continue the tree path), the buffered characters are flushed as-is and the parser resets to raw mode.
197
+
198
+ ---
199
+
200
+ ## Error Handling
201
+
202
+ | Condition | Behavior |
203
+ |-----------|----------|
204
+ | Empty start delimiter | `addPattern()` returns `false` |
205
+ | Empty end delimiter | `addPattern()` returns `false` |
206
+ | Empty input string | `parseString()` returns `''` |
207
+ | No matching patterns | Input is returned unchanged |
208
+ | Unmatched start delimiter | Start delimiter characters pass through as-is |
209
+ | Nested start delimiters | Inner start is treated as content, not a new match |
package/docs/cover.md ADDED
@@ -0,0 +1,15 @@
1
+ # Precedent
2
+
3
+ > A zero-dependency meta-templating engine for Node.js and the browser
4
+
5
+ Define arbitrary delimiter patterns and replace them with strings, computed values, or function output. Precedent powers the `{~...~}` expression system in Pict and the `${...}` environment variable substitution in Fable Settings.
6
+
7
+ - **Custom Delimiters** -- Register any start/end string pair as a pattern with `addPattern()`
8
+ - **Function Handlers** -- Patterns can replace with static strings or call a function with the matched content and a data argument
9
+ - **Pattern Precedence** -- A word tree ensures longer delimiters match before shorter ones (`<<EXTRA` before `<<` before `<`)
10
+ - **Zero Dependencies** -- No runtime dependencies; under 300 lines of code
11
+ - **Browser Ready** -- Includes a browser shim that assigns `window.Precedent`; builds with Quackage
12
+
13
+ [Quick Start](README.md)
14
+ [API Reference](api.md)
15
+ [GitHub](https://github.com/stevenvelozo/precedent)