@intentius/chant-lexicon-gitlab 0.0.11 → 0.0.12

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "xxhash64",
3
3
  "artifacts": {
4
- "manifest.json": "940e3afc86046e46",
4
+ "manifest.json": "6bd0f9deb20b8705",
5
5
  "meta.json": "c663c6c63748a9d0",
6
6
  "types/index.d.ts": "64e65524615be023",
7
7
  "rules/missing-stage.ts": "6d5379e74209a735",
@@ -11,11 +11,11 @@
11
11
  "rules/wgl011.ts": "b6b97e5104d91267",
12
12
  "rules/wgl015.ts": "d7e9e080994f985",
13
13
  "rules/wgl012.ts": "3d188d13fb2236c0",
14
- "rules/yaml-helpers.ts": "1f3c4e98b89b8deb",
14
+ "rules/yaml-helpers.ts": "b5416b80369484f2",
15
15
  "rules/wgl010.ts": "1548cad287cdf286",
16
16
  "rules/wgl014.ts": "6248a852888e8028",
17
17
  "rules/wgl013.ts": "3519c933e23fc605",
18
18
  "skills/chant-gitlab.md": "4393eb63e0b84b7f"
19
19
  },
20
- "composite": "3a7c7f06b1b16276"
20
+ "composite": "95f5813f9d4b37c7"
21
21
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitlab",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "GitLab",
6
6
  "intrinsics": [
@@ -7,14 +7,7 @@
7
7
  * without a full YAML parser dependency.
8
8
  */
9
9
 
10
- import type { SerializerResult } from "@intentius/chant/serializer";
11
-
12
- /**
13
- * Extract the primary output string from a serializer result.
14
- */
15
- export function getPrimaryOutput(output: string | SerializerResult): string {
16
- return typeof output === "string" ? output : output.primary;
17
- }
10
+ export { getPrimaryOutput } from "@intentius/chant/lint/post-synth";
18
11
 
19
12
  /**
20
13
  * Parse a serialized GitLab CI YAML into a structured object.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-gitlab",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": ["src/", "dist/"],
@@ -21,7 +21,7 @@
21
21
  "prepack": "bun run bundle && bun run validate"
22
22
  },
23
23
  "dependencies": {
24
- "@intentius/chant": "0.0.10"
24
+ "@intentius/chant": "0.0.11"
25
25
  },
26
26
  "devDependencies": {
27
27
  "typescript": "^5.9.3"
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { TemplateParser, TemplateIR, ResourceIR } from "@intentius/chant/import/parser";
9
+ import { parseYAML } from "@intentius/chant/yaml";
9
10
 
10
11
  /**
11
12
  * Reserved top-level keys in .gitlab-ci.yml that are NOT job definitions.
@@ -33,173 +34,6 @@ const RESERVED_KEYS = new Set([
33
34
  ]);
34
35
 
35
36
 
36
- /**
37
- * Parse a YAML document into a plain object.
38
- * Uses a simple YAML parser approach — GitLab CI YAML is straightforward
39
- * enough that we can parse it without a full YAML library by parsing JSON
40
- * or using Bun's built-in YAML support if available.
41
- */
42
- function parseYAML(content: string): Record<string, unknown> {
43
- // Try JSON first (some CI files may be JSON)
44
- try {
45
- return JSON.parse(content);
46
- } catch {
47
- // Fall through to YAML parsing
48
- }
49
-
50
- // Simple YAML parser for GitLab CI files
51
- // This handles the common cases: scalars, arrays, objects, block scalars
52
- const lines = content.split("\n");
53
- return parseYAMLLines(lines, 0, 0).value as Record<string, unknown>;
54
- }
55
-
56
- interface ParseResult {
57
- value: unknown;
58
- endIndex: number;
59
- }
60
-
61
- function parseYAMLLines(lines: string[], startIndex: number, baseIndent: number): ParseResult {
62
- const result: Record<string, unknown> = {};
63
- let i = startIndex;
64
-
65
- while (i < lines.length) {
66
- const line = lines[i];
67
- // Skip empty lines and comments
68
- if (line.trim() === "" || line.trim().startsWith("#")) {
69
- i++;
70
- continue;
71
- }
72
-
73
- const indent = line.search(/\S/);
74
- if (indent < baseIndent) break; // Dedented — done with this block
75
- if (indent > baseIndent && startIndex > 0) break; // Unexpected indent
76
-
77
- const keyMatch = line.match(/^(\s*)([^\s:][^:]*?):\s*(.*)$/);
78
- if (keyMatch) {
79
- const key = keyMatch[2].trim();
80
- const inlineValue = keyMatch[3].trim();
81
-
82
- if (inlineValue === "" || inlineValue.startsWith("#")) {
83
- // Check next line for array or nested object
84
- if (i + 1 < lines.length) {
85
- const nextLine = lines[i + 1];
86
- const nextIndent = nextLine.search(/\S/);
87
- if (nextIndent > indent && nextLine.trimStart().startsWith("- ")) {
88
- // Array
89
- const arr = parseYAMLArray(lines, i + 1, nextIndent);
90
- result[key] = arr.value;
91
- i = arr.endIndex;
92
- continue;
93
- } else if (nextIndent > indent) {
94
- // Nested object
95
- const nested = parseYAMLLines(lines, i + 1, nextIndent);
96
- result[key] = nested.value;
97
- i = nested.endIndex;
98
- continue;
99
- }
100
- }
101
- result[key] = null;
102
- i++;
103
- } else if (inlineValue.startsWith("[")) {
104
- // Inline array
105
- try {
106
- result[key] = JSON.parse(inlineValue);
107
- } catch {
108
- result[key] = inlineValue;
109
- }
110
- i++;
111
- } else if (inlineValue.startsWith("{")) {
112
- // Inline object
113
- try {
114
- result[key] = JSON.parse(inlineValue);
115
- } catch {
116
- result[key] = inlineValue;
117
- }
118
- i++;
119
- } else {
120
- result[key] = parseScalar(inlineValue);
121
- i++;
122
- }
123
- } else if (line.trimStart().startsWith("- ")) {
124
- // We hit an array at the top level — shouldn't happen normally
125
- break;
126
- } else {
127
- i++;
128
- }
129
- }
130
-
131
- return { value: result, endIndex: i };
132
- }
133
-
134
- function parseYAMLArray(lines: string[], startIndex: number, baseIndent: number): ParseResult {
135
- const result: unknown[] = [];
136
- let i = startIndex;
137
-
138
- while (i < lines.length) {
139
- const line = lines[i];
140
- if (line.trim() === "" || line.trim().startsWith("#")) {
141
- i++;
142
- continue;
143
- }
144
-
145
- const indent = line.search(/\S/);
146
- if (indent < baseIndent) break;
147
-
148
- const itemMatch = line.match(/^(\s*)- (.*)$/);
149
- if (itemMatch && indent === baseIndent) {
150
- const itemValue = itemMatch[2].trim();
151
- // Check if it's a key-value pair (object item in array)
152
- const kvMatch = itemValue.match(/^([^\s:][^:]*?):\s*(.*)$/);
153
- if (kvMatch) {
154
- const obj: Record<string, unknown> = {};
155
- obj[kvMatch[1].trim()] = parseScalar(kvMatch[2].trim());
156
- // Check for more keys at indent+2
157
- const nextIndent = indent + 2;
158
- let j = i + 1;
159
- while (j < lines.length) {
160
- const nextLine = lines[j];
161
- if (nextLine.trim() === "" || nextLine.trim().startsWith("#")) {
162
- j++;
163
- continue;
164
- }
165
- const ni = nextLine.search(/\S/);
166
- if (ni !== nextIndent) break;
167
- const nextKV = nextLine.match(/^(\s*)([^\s:][^:]*?):\s*(.*)$/);
168
- if (nextKV) {
169
- obj[nextKV[2].trim()] = parseScalar(nextKV[3].trim());
170
- j++;
171
- } else {
172
- break;
173
- }
174
- }
175
- result.push(obj);
176
- i = j;
177
- } else {
178
- result.push(parseScalar(itemValue));
179
- i++;
180
- }
181
- } else {
182
- break;
183
- }
184
- }
185
-
186
- return { value: result, endIndex: i };
187
- }
188
-
189
- function parseScalar(value: string): unknown {
190
- if (value === "" || value === "~" || value === "null") return null;
191
- if (value === "true" || value === "yes") return true;
192
- if (value === "false" || value === "no") return false;
193
- // Strip quotes
194
- if ((value.startsWith("'") && value.endsWith("'")) || (value.startsWith('"') && value.endsWith('"'))) {
195
- return value.slice(1, -1);
196
- }
197
- // Number
198
- const num = Number(value);
199
- if (!isNaN(num) && value !== "") return num;
200
- return value;
201
- }
202
-
203
37
  /**
204
38
  * GitLab CI YAML parser implementation.
205
39
  */
@@ -7,14 +7,7 @@
7
7
  * without a full YAML parser dependency.
8
8
  */
9
9
 
10
- import type { SerializerResult } from "@intentius/chant/serializer";
11
-
12
- /**
13
- * Extract the primary output string from a serializer result.
14
- */
15
- export function getPrimaryOutput(output: string | SerializerResult): string {
16
- return typeof output === "string" ? output : output.primary;
17
- }
10
+ export { getPrimaryOutput } from "@intentius/chant/lint/post-synth";
18
11
 
19
12
  /**
20
13
  * Parse a serialized GitLab CI YAML into a structured object.
package/src/serializer.ts CHANGED
@@ -13,6 +13,7 @@ import type { Serializer } from "@intentius/chant/serializer";
13
13
  import type { LexiconOutput } from "@intentius/chant/lexicon-output";
14
14
  import { walkValue, type SerializerVisitor } from "@intentius/chant/serializer-walker";
15
15
  import { INTRINSIC_MARKER } from "@intentius/chant/intrinsic";
16
+ import { emitYAML } from "@intentius/chant/yaml";
16
17
 
17
18
  /**
18
19
  * GitLab CI visitor for the generic serializer walker.
@@ -82,111 +83,6 @@ function toYAMLValue(value: unknown, entityNames: Map<Declarable, string>): unkn
82
83
  return walkValue(preprocessed, entityNames, gitlabVisitor(entityNames));
83
84
  }
84
85
 
85
- /**
86
- * Emit a YAML value with proper indentation.
87
- */
88
- function emitYAML(value: unknown, indent: number): string {
89
- const prefix = " ".repeat(indent);
90
-
91
- if (value === null || value === undefined) {
92
- return "null";
93
- }
94
-
95
- if (typeof value === "boolean") {
96
- return value ? "true" : "false";
97
- }
98
-
99
- if (typeof value === "number") {
100
- return String(value);
101
- }
102
-
103
- if (typeof value === "string") {
104
- // Quote strings that could be misinterpreted
105
- if (
106
- value === "" ||
107
- value === "true" ||
108
- value === "false" ||
109
- value === "null" ||
110
- value === "yes" ||
111
- value === "no" ||
112
- value.includes(": ") ||
113
- value.includes("#") ||
114
- value.startsWith("*") ||
115
- value.startsWith("&") ||
116
- value.startsWith("!") ||
117
- value.startsWith("{") ||
118
- value.startsWith("[") ||
119
- value.startsWith("'") ||
120
- value.startsWith('"') ||
121
- value.startsWith("$") ||
122
- /^\d/.test(value)
123
- ) {
124
- // Use single quotes, escaping internal single quotes
125
- return `'${value.replace(/'/g, "''")}'`;
126
- }
127
- return value;
128
- }
129
-
130
- if (Array.isArray(value)) {
131
- if (value.length === 0) return "[]";
132
- const lines: string[] = [];
133
- for (const item of value) {
134
- if (typeof item === "object" && item !== null && !Array.isArray(item)) {
135
- // Object items in arrays
136
- const entries = Object.entries(item as Record<string, unknown>);
137
- if (entries.length > 0) {
138
- const [firstKey, firstVal] = entries[0];
139
- const firstEmitted = emitYAML(firstVal, indent + 2);
140
- if (firstEmitted.startsWith("\n")) {
141
- // Multi-line value: put on next line, indented under the key
142
- lines.push(`${prefix}- ${firstKey}:${firstEmitted}`);
143
- } else {
144
- lines.push(`${prefix}- ${firstKey}: ${firstEmitted}`);
145
- }
146
- for (let i = 1; i < entries.length; i++) {
147
- const [key, val] = entries[i];
148
- const emitted = emitYAML(val, indent + 2);
149
- if (emitted.startsWith("\n")) {
150
- lines.push(`${prefix} ${key}:${emitted}`);
151
- } else {
152
- lines.push(`${prefix} ${key}: ${emitted}`);
153
- }
154
- }
155
- }
156
- } else {
157
- lines.push(`${prefix}- ${emitYAML(item, indent + 1).trimStart()}`);
158
- }
159
- }
160
- return "\n" + lines.join("\n");
161
- }
162
-
163
- if (typeof value === "object") {
164
- const obj = value as Record<string, unknown>;
165
-
166
- // Handle tagged values (intrinsics like !reference)
167
- if ("tag" in obj && "value" in obj && typeof obj.tag === "string") {
168
- if (obj.tag === "!reference" && Array.isArray(obj.value)) {
169
- return `!reference [${(obj.value as string[]).join(", ")}]`;
170
- }
171
- }
172
-
173
- const entries = Object.entries(obj);
174
- if (entries.length === 0) return "{}";
175
- const lines: string[] = [];
176
- for (const [key, val] of entries) {
177
- const emitted = emitYAML(val, indent + 1);
178
- if (emitted.startsWith("\n")) {
179
- lines.push(`${prefix}${key}:${emitted}`);
180
- } else {
181
- lines.push(`${prefix}${key}: ${emitted}`);
182
- }
183
- }
184
- return "\n" + lines.join("\n");
185
- }
186
-
187
- return String(value);
188
- }
189
-
190
86
  /**
191
87
  * GitLab CI YAML serializer implementation.
192
88
  */