@sprig-and-prose/sprig-universe 0.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/PHILOSOPHY.md +201 -0
- package/README.md +168 -0
- package/REFERENCE.md +355 -0
- package/biome.json +24 -0
- package/package.json +30 -0
- package/repositories/sprig-repository-github/index.js +29 -0
- package/src/ast.js +257 -0
- package/src/cli.js +1510 -0
- package/src/graph.js +950 -0
- package/src/index.js +46 -0
- package/src/ir.js +121 -0
- package/src/parser.js +1656 -0
- package/src/scanner.js +255 -0
- package/src/scene-manifest.js +856 -0
- package/src/util/span.js +46 -0
- package/src/util/text.js +126 -0
- package/src/validator.js +862 -0
- package/src/validators/mysql/connection.js +154 -0
- package/src/validators/mysql/schema.js +209 -0
- package/src/validators/mysql/type-compat.js +219 -0
- package/src/validators/mysql/validator.js +332 -0
- package/test/fixtures/amaranthine-mini.prose +53 -0
- package/test/fixtures/conflicting-universes-a.prose +8 -0
- package/test/fixtures/conflicting-universes-b.prose +8 -0
- package/test/fixtures/duplicate-names.prose +20 -0
- package/test/fixtures/first-line-aware.prose +32 -0
- package/test/fixtures/indented-describe.prose +18 -0
- package/test/fixtures/multi-file-universe-a.prose +15 -0
- package/test/fixtures/multi-file-universe-b.prose +15 -0
- package/test/fixtures/multi-file-universe-conflict-desc.prose +12 -0
- package/test/fixtures/multi-file-universe-conflict-title.prose +4 -0
- package/test/fixtures/multi-file-universe-with-title.prose +10 -0
- package/test/fixtures/named-document.prose +17 -0
- package/test/fixtures/named-duplicate.prose +22 -0
- package/test/fixtures/named-reference.prose +17 -0
- package/test/fixtures/relates-errors.prose +38 -0
- package/test/fixtures/relates-tier1.prose +14 -0
- package/test/fixtures/relates-tier2.prose +16 -0
- package/test/fixtures/relates-tier3.prose +21 -0
- package/test/fixtures/sprig-meta-mini.prose +62 -0
- package/test/fixtures/unresolved-relates.prose +15 -0
- package/test/fixtures/using-in-references.prose +35 -0
- package/test/fixtures/using-unknown.prose +8 -0
- package/test/universe-basic.test.js +804 -0
- package/tsconfig.json +15 -0
package/src/util/span.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Source span utilities for tracking source locations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} SourceSpan
|
|
7
|
+
* @property {string} file - File path
|
|
8
|
+
* @property {{ line: number, col: number, offset: number }} start - Start position
|
|
9
|
+
* @property {{ line: number, col: number, offset: number }} end - End position
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates a source span from start and end positions
|
|
14
|
+
* @param {string} file - File path
|
|
15
|
+
* @param {{ line: number, col: number, offset: number }} start - Start position
|
|
16
|
+
* @param {{ line: number, col: number, offset: number }} end - End position
|
|
17
|
+
* @returns {SourceSpan}
|
|
18
|
+
*/
|
|
19
|
+
export function createSpan(file, start, end) {
|
|
20
|
+
return { file, start, end };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates a zero-length span at a position
|
|
25
|
+
* @param {string} file - File path
|
|
26
|
+
* @param {{ line: number, col: number, offset: number }} pos - Position
|
|
27
|
+
* @returns {SourceSpan}
|
|
28
|
+
*/
|
|
29
|
+
export function createPointSpan(file, pos) {
|
|
30
|
+
return { file, start: pos, end: pos };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Merges two spans (from start of first to end of second)
|
|
35
|
+
* @param {SourceSpan} span1 - First span
|
|
36
|
+
* @param {SourceSpan} span2 - Second span
|
|
37
|
+
* @returns {SourceSpan}
|
|
38
|
+
*/
|
|
39
|
+
export function mergeSpans(span1, span2) {
|
|
40
|
+
return {
|
|
41
|
+
file: span1.file,
|
|
42
|
+
start: span1.start,
|
|
43
|
+
end: span2.end,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
package/src/util/text.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Text normalization utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Counts leading whitespace, treating tabs as 2 spaces.
|
|
7
|
+
* @param {string} line - Line to count indentation for
|
|
8
|
+
* @returns {number} - Indentation count (spaces + tabs*2)
|
|
9
|
+
*/
|
|
10
|
+
function countIndent(line) {
|
|
11
|
+
let count = 0;
|
|
12
|
+
for (let i = 0; i < line.length; i++) {
|
|
13
|
+
if (line[i] === ' ') {
|
|
14
|
+
count++;
|
|
15
|
+
} else if (line[i] === '\t') {
|
|
16
|
+
count += 2; // Treat tabs as 2 spaces
|
|
17
|
+
} else {
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return count;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Removes exactly `amount` worth of leading whitespace (spaces/tabs).
|
|
26
|
+
* Treats tabs as 2 spaces when removing.
|
|
27
|
+
* @param {string} line - Line to remove indentation from
|
|
28
|
+
* @param {number} amount - Amount of indentation to remove (in space units)
|
|
29
|
+
* @returns {string} - Line with indentation removed
|
|
30
|
+
*/
|
|
31
|
+
function removeIndent(line, amount) {
|
|
32
|
+
let removed = 0;
|
|
33
|
+
let i = 0;
|
|
34
|
+
while (i < line.length && removed < amount) {
|
|
35
|
+
if (line[i] === ' ') {
|
|
36
|
+
removed++;
|
|
37
|
+
i++;
|
|
38
|
+
} else if (line[i] === '\t') {
|
|
39
|
+
removed += 2; // Treat tabs as 2 spaces
|
|
40
|
+
i++;
|
|
41
|
+
// If we overshoot, we've removed the tab, which is fine
|
|
42
|
+
} else {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return line.slice(i);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Normalizes prose blocks by removing common indentation from lines after the first non-empty line.
|
|
51
|
+
* This handles the common case where the first line is flush-left (indent 0) and subsequent lines
|
|
52
|
+
* are indented due to code formatting.
|
|
53
|
+
*
|
|
54
|
+
* Algorithm:
|
|
55
|
+
* 1. Split raw by '\n' into lines
|
|
56
|
+
* 2. Find the first non-empty line index i0
|
|
57
|
+
* 3. Compute minIndentAfter from lines AFTER i0 (only non-empty lines)
|
|
58
|
+
* 4. Keep lines[0..i0] unchanged, then for lines after i0, remove minIndentAfter worth of indentation
|
|
59
|
+
*
|
|
60
|
+
* @param {string} raw - Raw text to normalize
|
|
61
|
+
* @returns {string} - Normalized text
|
|
62
|
+
*/
|
|
63
|
+
export function normalizeProseBlock(raw) {
|
|
64
|
+
const lines = raw.split('\n');
|
|
65
|
+
|
|
66
|
+
// Find first non-empty line index
|
|
67
|
+
let i0 = -1;
|
|
68
|
+
for (let i = 0; i < lines.length; i++) {
|
|
69
|
+
if (lines[i].trim().length > 0) {
|
|
70
|
+
i0 = i;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// If no non-empty line found, return raw unchanged
|
|
76
|
+
if (i0 === -1) {
|
|
77
|
+
return raw;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Compute minIndentAfter from lines AFTER i0 (only non-empty lines)
|
|
81
|
+
// Use the minimum indent to preserve relative indentation while removing common prefix
|
|
82
|
+
let minIndentAfter = null;
|
|
83
|
+
for (let i = i0 + 1; i < lines.length; i++) {
|
|
84
|
+
if (lines[i].trim().length > 0) {
|
|
85
|
+
const indent = countIndent(lines[i]);
|
|
86
|
+
if (minIndentAfter === null || indent < minIndentAfter) {
|
|
87
|
+
minIndentAfter = indent;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// If no non-empty lines after i0, return raw unchanged
|
|
93
|
+
if (minIndentAfter === null || minIndentAfter === 0) {
|
|
94
|
+
return raw;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Produce output: keep lines[0..i0] unchanged, then remove minIndentAfter from subsequent lines
|
|
98
|
+
const result = [];
|
|
99
|
+
for (let i = 0; i < lines.length; i++) {
|
|
100
|
+
if (i <= i0) {
|
|
101
|
+
// Keep lines up to and including first non-empty line unchanged
|
|
102
|
+
result.push(lines[i]);
|
|
103
|
+
} else {
|
|
104
|
+
// For lines after i0
|
|
105
|
+
if (lines[i].trim().length === 0) {
|
|
106
|
+
// Empty/whitespace-only lines: keep as empty string
|
|
107
|
+
result.push('');
|
|
108
|
+
} else {
|
|
109
|
+
// Remove exactly minIndentAfter worth of indentation
|
|
110
|
+
result.push(removeIndent(lines[i], minIndentAfter));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return result.join('\n');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @deprecated Use normalizeProseBlock instead
|
|
120
|
+
* @param {string} raw - Raw text to dedent
|
|
121
|
+
* @returns {string} - Dedented text
|
|
122
|
+
*/
|
|
123
|
+
export function dedentPreserve(raw) {
|
|
124
|
+
return normalizeProseBlock(raw);
|
|
125
|
+
}
|
|
126
|
+
|