@salesforcedevs/sfdocs-liquid-lint-capture 0.0.1-alpha

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 ADDED
@@ -0,0 +1,173 @@
1
+ # liquid-lint-capture
2
+
3
+ This Markdown plugin validates the correct usage of Liquid capture blocks based on file location.
4
+
5
+ ## What it checks
6
+
7
+ ### For files in `shared/partials/`:
8
+
9
+ 1. **Content must be in capture blocks**: All markdown content (except comments) must be inside `{% capture %}` blocks
10
+ 2. **No duplicate capture names**: Each capture variable name must be unique within the file
11
+ 3. **Allowed content outside capture blocks**:
12
+ - HTML comments: `<!-- comment -->`
13
+ - Liquid comments: `{% comment %}...{% endcomment %}`
14
+ - Whitespace
15
+
16
+ ### For files outside `shared/partials/`:
17
+
18
+ 1. **Capture blocks are not allowed**: Files outside the partials folder must not contain `{% capture %}` blocks
19
+
20
+ ## Why
21
+
22
+ Partial files in `shared/partials/` are reusable snippets that define Liquid variables via capture blocks. These variables are then consumed by other markdown files through includes. Enforcing this structure ensures:
23
+
24
+ - Predictable variable definitions
25
+ - No unintended content rendering
26
+ - Prevent variable name collisions
27
+ - Clear separation between partials and regular content
28
+
29
+ Regular content files outside `shared/partials/` should not use capture blocks, as they are meant to render content directly, not define reusable variables. This validation prevents:
30
+
31
+ - Misplaced variable definitions in content files
32
+ - Confusion about file purpose and behavior
33
+ - Accidental capture block usage outside the partials system
34
+
35
+ ## Examples
36
+
37
+ ### ✅ Valid
38
+
39
+ **Single capture block:**
40
+ ```markdown
41
+ {% capture my_variable %}
42
+ # Heading
43
+
44
+ Content goes here.
45
+ {% endcapture %}
46
+ ```
47
+
48
+ **Multiple captures with comments:**
49
+ ```markdown
50
+ <!-- This file defines common callouts -->
51
+
52
+ {% comment %}Success message{% endcomment %}
53
+ {% capture success_msg %}
54
+ :::tip
55
+ Operation completed successfully!
56
+ :::
57
+ {% endcapture %}
58
+
59
+ {% capture error_msg %}
60
+ :::warning
61
+ An error occurred.
62
+ :::
63
+ {% endcapture %}
64
+ ```
65
+
66
+ **Capture with whitespace control:**
67
+ ```markdown
68
+ {%- capture trimmed_content -%}
69
+ No extra whitespace before/after
70
+ {%- endcapture -%}
71
+ ```
72
+
73
+ ### ❌ Invalid
74
+
75
+ **Content outside capture block:**
76
+ ```markdown
77
+ # This heading is not captured
78
+
79
+ {% capture my_var %}
80
+ Captured content
81
+ {% endcapture %}
82
+ ```
83
+ Error: `Content in shared/partials files must be inside capture blocks.`
84
+
85
+ **Duplicate capture names:**
86
+ ```markdown
87
+ {% capture my_variable %}
88
+ First definition
89
+ {% endcapture %}
90
+
91
+ {% capture my_variable %}
92
+ Duplicate definition
93
+ {% endcapture %}
94
+ ```
95
+ Error: `Duplicate capture block name "my_variable". This variable already exists in file.`
96
+
97
+ **Mixed issues:**
98
+ ```markdown
99
+ Regular paragraph outside capture.
100
+
101
+ {% capture my_var %}First{% endcapture %}
102
+ {% capture my_var %}Duplicate{% endcapture %}
103
+ ```
104
+ Errors:
105
+ - `Content in shared/partials files must be inside capture blocks.`
106
+ - `Duplicate capture block name "my_var"`
107
+
108
+ ### ❌ Invalid (Files outside shared/partials)
109
+
110
+ **Capture block in regular content file:**
111
+ ```markdown
112
+ # Guide Title
113
+
114
+ {% capture my_variable %}
115
+ Some content
116
+ {% endcapture %}
117
+
118
+ Regular content here.
119
+ ```
120
+ Error: `Capture blocks are not allowed in files outside shared/partials folder.`
121
+
122
+ ## File Scope
123
+
124
+ This rule applies to **all** markdown files with different validation rules based on location:
125
+
126
+ ### Files in `shared/partials/`:
127
+ - **Must** use capture blocks for all content
128
+ - **Must** have unique capture block names
129
+ - `/content/shared/partials/common.md` ✅
130
+ - `/docs/shared/partials/alerts.md` ✅
131
+ - `shared/partials/buttons.md` ✅
132
+
133
+ ### Files outside `shared/partials/`:
134
+ - **Must not** contain capture blocks
135
+ - `/content/guides/example.md` ✅
136
+ - `/shared/snippets/code.md` ✅
137
+ - `docs/tutorials/intro.md` ✅
138
+
139
+ ## Install & build
140
+
141
+ ```bash
142
+ yarn install && yarn build
143
+ ```
144
+
145
+ ## Testing
146
+
147
+ ```bash
148
+ yarn test
149
+ ```
150
+
151
+ ## Usage
152
+
153
+ In your unified pipeline:
154
+
155
+ ```typescript
156
+ import unified from 'unified';
157
+ import parse from 'remark-parse';
158
+ import liquidLintPartialsCapture from '@salesforcedevs/sfdocs-liquid-lint-capture';
159
+
160
+ const processor = unified()
161
+ .use(parse)
162
+ .use(liquidLintPartialsCapture);
163
+
164
+ const file = await processor.process(markdownContent);
165
+
166
+ if (file.messages.length > 0) {
167
+ file.messages.forEach(msg => {
168
+ console.log(`${msg.line}:${msg.column}: ${msg.message}`);
169
+ });
170
+ }
171
+ ```
172
+
173
+ ---
@@ -0,0 +1,11 @@
1
+ /**
2
+ * For .md files in shared/partials folder,
3
+ * all content must be in capture blocks
4
+ * only HTML and Liquid comments are allowed outside capture blocks
5
+ * we should not have duplicate names for capture blocks
6
+ *
7
+ * For .md files outside shared/partials folder, a file must not contain capture block
8
+ */
9
+ declare const _default: any;
10
+ export = _default;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;;AA+HH,kBAA0C"}
package/dist/index.js ADDED
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ /**
3
+ * For .md files in shared/partials folder,
4
+ * all content must be in capture blocks
5
+ * only HTML and Liquid comments are allowed outside capture blocks
6
+ * we should not have duplicate names for capture blocks
7
+ *
8
+ * For .md files outside shared/partials folder, a file must not contain capture block
9
+ */
10
+ var __importDefault = (this && this.__importDefault) || function (mod) {
11
+ return (mod && mod.__esModule) ? mod : { "default": mod };
12
+ };
13
+ const unified_lint_rule_1 = __importDefault(require("unified-lint-rule"));
14
+ const sfdocs_liquid_lint_utils_1 = require("@salesforcedevs/sfdocs-liquid-lint-utils");
15
+ const SOURCE = 'sfdocs-liquid-lint:liquid-capture';
16
+ // Regex patterns
17
+ const CAPTURE_BLOCK_RE = /{%-?\s*capture\s+(\w+)\s*-?%}([\s\S]*?){%-?\s*endcapture\s*-?%}/g;
18
+ const HTML_COMMENT_RE = /<!--[\s\S]*?-->/g;
19
+ const LIQUID_COMMENT_RE = /{%-?\s*comment\s*-?%}[\s\S]*?{%-?\s*endcomment\s*-?%}/g;
20
+ function isInPartialsFolder(filePath) {
21
+ return filePath.includes('/shared/partials/');
22
+ }
23
+ function extractCaptureBlocks(content) {
24
+ const captures = [];
25
+ const re = new RegExp(CAPTURE_BLOCK_RE.source, 'g');
26
+ let match;
27
+ while ((match = re.exec(content)) !== null) {
28
+ captures.push({
29
+ name: match[1],
30
+ startOffset: match.index,
31
+ endOffset: match.index + match[0].length,
32
+ });
33
+ }
34
+ return captures;
35
+ }
36
+ function findDuplicateCaptures(captures) {
37
+ const nameMap = new Map();
38
+ for (const capture of captures) {
39
+ if (!nameMap.has(capture.name)) {
40
+ nameMap.set(capture.name, []);
41
+ }
42
+ nameMap.get(capture.name).push(capture);
43
+ }
44
+ // Filter to only duplicates
45
+ const duplicates = new Map();
46
+ for (const [name, blocks] of nameMap.entries()) {
47
+ if (blocks.length > 1) {
48
+ duplicates.set(name, blocks);
49
+ }
50
+ }
51
+ return duplicates;
52
+ }
53
+ function removeAllowedContent(content) {
54
+ let cleaned = content;
55
+ cleaned = cleaned.replace(CAPTURE_BLOCK_RE, '');
56
+ cleaned = cleaned.replace(HTML_COMMENT_RE, '');
57
+ cleaned = cleaned.replace(LIQUID_COMMENT_RE, '');
58
+ return cleaned;
59
+ }
60
+ function hasDisallowedContent(cleaned) {
61
+ return /\S/.test(cleaned);
62
+ }
63
+ function checkLiquidCapture(_tree, file) {
64
+ var _a;
65
+ if (!file.path)
66
+ return;
67
+ const raw = String((_a = file.contents) !== null && _a !== void 0 ? _a : '');
68
+ if (!raw)
69
+ return;
70
+ const captureBlocks = extractCaptureBlocks(raw);
71
+ if (!isInPartialsFolder(file.path)) {
72
+ if (captureBlocks.length > 0) {
73
+ for (const capture of captureBlocks) {
74
+ const position = (0, sfdocs_liquid_lint_utils_1.offsetToPosition)(raw, capture.startOffset);
75
+ file.message(`Capture blocks are not allowed in files outside shared/partials folder. Found capture block "${capture.name}".`, position);
76
+ }
77
+ }
78
+ return;
79
+ }
80
+ const duplicates = findDuplicateCaptures(captureBlocks);
81
+ if (duplicates.size > 0) {
82
+ for (const [name, blocks] of duplicates.entries()) {
83
+ // Report on all occurrences after the first
84
+ for (let i = 1; i < blocks.length; i++) {
85
+ const position = (0, sfdocs_liquid_lint_utils_1.offsetToPosition)(raw, blocks[i].startOffset);
86
+ file.message(`Duplicate capture block name "${name}". This variable already exists in file.`, position);
87
+ }
88
+ }
89
+ }
90
+ // Check for disallowed content outside capture blocks and comments
91
+ const cleaned = removeAllowedContent(raw);
92
+ if (hasDisallowedContent(cleaned)) {
93
+ // Find the first line with disallowed content for better error reporting
94
+ const lines = cleaned.split('\n');
95
+ for (const line of lines) {
96
+ if (/\S/.test(line)) {
97
+ file.message(`Content in shared/partials files must be inside capture blocks. Found content outside capture blocks: "${line.trim().substring(0, 50)}..."`);
98
+ break;
99
+ }
100
+ }
101
+ }
102
+ }
103
+ module.exports = (0, unified_lint_rule_1.default)(SOURCE, checkLiquidCapture);
104
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/jest.config.js ADDED
@@ -0,0 +1,16 @@
1
+ const BASE_CONFIG = require('../../scripts/jest/common.config');
2
+
3
+ module.exports = {
4
+ ...BASE_CONFIG,
5
+ displayName: 'liquid-lint-capture',
6
+ collectCoverageFrom: ['<rootDir>/**/src/*.{ts,js}'],
7
+ coveragePathIgnorePatterns: ['/node_modules/', '/dist/'],
8
+ testMatch: ['<rootDir>/**/__tests__/**/*.{test,spec}.ts'],
9
+ testPathIgnorePatterns: ['lib/', 'node_modules/'],
10
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
11
+ preset: 'ts-jest',
12
+ testEnvironment: 'node',
13
+ transform: {
14
+ '^.+\\.ts$': 'ts-jest',
15
+ },
16
+ };
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@salesforcedevs/sfdocs-liquid-lint-capture",
3
+ "version": "0.0.1-alpha",
4
+ "description": "Validates '{% capture tag_name %} \"...\" {% endcapture %}' Liquid capture under shared/partials markdown files",
5
+ "author": "SFDocs Team",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "yarn compile",
9
+ "compile": "tsc -p tsconfig.json",
10
+ "prepublish": "yarn build",
11
+ "test": "jest",
12
+ "test:ci": "yarn test --maxWorkers=1 --ci --coverage",
13
+ "watch": "tsc --watch"
14
+ },
15
+ "license": "MIT",
16
+ "dependencies": {
17
+ "@salesforcedevs/sfdocs-liquid-lint-utils": "^1.0.0",
18
+ "unified-lint-rule": "^1.0.6",
19
+ "vfile": "^4.2.1"
20
+ },
21
+ "devDependencies": {
22
+ "@types/jest": "^25.2.2",
23
+ "@types/node": "^15.6.1",
24
+ "dedent": "^0.7.0",
25
+ "jest": "^29.7.0",
26
+ "ts-jest": "^29.2.5",
27
+ "typescript": "^4.4.2",
28
+ "unified": "^9.0.0"
29
+ }
30
+ }
@@ -0,0 +1,451 @@
1
+ import dedent from 'dedent';
2
+ import unified from 'unified';
3
+ import markdown from 'remark-parse';
4
+ import stringify from 'remark-stringify';
5
+ import vfile, { VFile } from 'vfile';
6
+ import plugin from '../index';
7
+
8
+ const processMarkdown = (md: string, filePath: string): VFile => {
9
+ const file = vfile({ path: filePath, contents: md });
10
+ return unified()
11
+ .use(markdown)
12
+ .use(plugin as unified.Attacher)
13
+ .use(stringify)
14
+ .processSync(file);
15
+ };
16
+
17
+ describe('sfdocs-liquid-lint:partials-capture', () => {
18
+ describe('file path filtering', () => {
19
+ test('ignores regular content in files not in shared/partials folder', () => {
20
+ const md = dedent(`
21
+ # This is a regular heading
22
+
23
+ Some content here.
24
+ `);
25
+ const result = processMarkdown(md, '/content/guides/example.md');
26
+ expect(result.messages).toHaveLength(0);
27
+ });
28
+
29
+ test('disallows capture blocks in files outside shared/partials folder', () => {
30
+ const md = dedent(`
31
+ # This is a regular heading
32
+
33
+ {% capture my_var %}
34
+ Some content
35
+ {% endcapture %}
36
+ `);
37
+ const result = processMarkdown(md, '/content/guides/example.md');
38
+ expect(result.messages).toHaveLength(1);
39
+ expect(result.messages[0].message).toContain('Capture blocks are not allowed in files outside shared/partials folder');
40
+ expect(result.messages[0].message).toContain('my_var');
41
+ });
42
+
43
+ test('reports all capture blocks in non-partials files', () => {
44
+ const md = dedent(`
45
+ {% capture var1 %}Content 1{% endcapture %}
46
+ {% capture var2 %}Content 2{% endcapture %}
47
+ {% capture var3 %}Content 3{% endcapture %}
48
+ `);
49
+ const result = processMarkdown(md, '/docs/guides/example.md');
50
+ expect(result.messages).toHaveLength(3);
51
+ expect(result.messages[0].message).toContain('var1');
52
+ expect(result.messages[1].message).toContain('var2');
53
+ expect(result.messages[2].message).toContain('var3');
54
+ });
55
+
56
+ test('allows normal content but disallows only captures in non-partials', () => {
57
+ const md = dedent(`
58
+ # Normal Heading
59
+
60
+ Regular paragraph content with **formatting**.
61
+
62
+ - List item 1
63
+ - List item 2
64
+
65
+ {% capture should_fail %}Captured content{% endcapture %}
66
+
67
+ More normal content here.
68
+
69
+ {% capture also_fails %}Another capture{% endcapture %}
70
+ `);
71
+ const result = processMarkdown(md, '/docs/guide.md');
72
+ expect(result.messages).toHaveLength(2);
73
+ expect(result.messages[0].message).toContain('not allowed in files outside shared/partials');
74
+ expect(result.messages[0].message).toContain('should_fail');
75
+ expect(result.messages[1].message).toContain('also_fails');
76
+ });
77
+
78
+ test('reports correct line positions for captures in non-partials', () => {
79
+ const md = dedent(`
80
+ # Title
81
+
82
+ {% capture var1 %}First{% endcapture %}
83
+
84
+ Some content
85
+
86
+ {% capture var2 %}Second{% endcapture %}
87
+ `);
88
+ const result = processMarkdown(md, '/docs/page.md');
89
+ expect(result.messages).toHaveLength(2);
90
+ expect(result.messages[0].line).toBe(3);
91
+ expect(result.messages[1].line).toBe(7);
92
+ });
93
+
94
+ test('validates files in shared/partials folder', () => {
95
+ const md = dedent(`
96
+ # This is a heading
97
+ `);
98
+ const result = processMarkdown(md, '/content/shared/partials/example.md');
99
+ expect(result.messages.length).toBeGreaterThan(0);
100
+ expect(result.messages[0].message).toContain('must be inside capture blocks');
101
+ });
102
+ });
103
+
104
+ describe('valid capture blocks in partials', () => {
105
+ test('allows single capture block', () => {
106
+ const md = dedent(`
107
+ {% capture my_variable %}
108
+ This is captured content.
109
+ {% endcapture %}
110
+ `);
111
+ const result = processMarkdown(md, '/shared/partials/example.md');
112
+ expect(result.messages).toHaveLength(0);
113
+ });
114
+
115
+ test('allows multiple capture blocks with different names', () => {
116
+ const md = dedent(`
117
+ {% capture var1 %}
118
+ Content 1
119
+ {% endcapture %}
120
+
121
+ {% capture var2 %}
122
+ Content 2
123
+ {% endcapture %}
124
+
125
+ {% capture var3 %}
126
+ Content 3
127
+ {% endcapture %}
128
+ `);
129
+ const result = processMarkdown(md, '/shared/partials/example.md');
130
+ expect(result.messages).toHaveLength(0);
131
+ });
132
+
133
+ test('allows capture blocks with whitespace control', () => {
134
+ const md = dedent(`
135
+ {%- capture my_variable -%}
136
+ Trimmed content
137
+ {%- endcapture -%}
138
+ `);
139
+ const result = processMarkdown(md, '/shared/partials/example.md');
140
+ expect(result.messages).toHaveLength(0);
141
+ });
142
+
143
+ test('disallows whitespace-controlled captures in non-partials', () => {
144
+ const md = '{%- capture my_var -%}Content{%- endcapture -%}';
145
+ const result = processMarkdown(md, '/content/docs/page.md');
146
+ expect(result.messages).toHaveLength(1);
147
+ expect(result.messages[0].message).toContain('not allowed in files outside shared/partials');
148
+ expect(result.messages[0].message).toContain('my_var');
149
+ });
150
+
151
+ test('allows capture blocks with markdown content inside', () => {
152
+ const md = dedent(`
153
+ {% capture rich_content %}
154
+ # Heading inside capture
155
+
156
+ This is a paragraph with **bold** and *italic*.
157
+
158
+ - List item 1
159
+ - List item 2
160
+ {% endcapture %}
161
+ `);
162
+ const result = processMarkdown(md, '/shared/partials/example.md');
163
+ expect(result.messages).toHaveLength(0);
164
+ });
165
+ });
166
+
167
+ describe('comments', () => {
168
+ test('allows HTML comments', () => {
169
+ const md = dedent(`
170
+ <!-- This is an HTML comment -->
171
+ {% capture my_var %}Content{% endcapture %}
172
+ `);
173
+ const result = processMarkdown(md, '/shared/partials/example.md');
174
+ expect(result.messages).toHaveLength(0);
175
+ });
176
+
177
+ test('allows multi-line HTML comments', () => {
178
+ const md = dedent(`
179
+ <!--
180
+ This is a multi-line
181
+ HTML comment
182
+ -->
183
+ {% capture my_var %}Content{% endcapture %}
184
+ `);
185
+ const result = processMarkdown(md, '/shared/partials/example.md');
186
+ expect(result.messages).toHaveLength(0);
187
+ });
188
+
189
+ test('allows Liquid comment blocks', () => {
190
+ const md = dedent(`
191
+ {% comment %}
192
+ This is a Liquid comment
193
+ {% endcomment %}
194
+ {% capture my_var %}Content{% endcapture %}
195
+ `);
196
+ const result = processMarkdown(md, '/shared/partials/example.md');
197
+ expect(result.messages).toHaveLength(0);
198
+ });
199
+
200
+ test('allows Liquid comments with whitespace control', () => {
201
+ const md = dedent(`
202
+ {%- comment -%}
203
+ Trimmed comment
204
+ {%- endcomment -%}
205
+ {% capture my_var %}Content{% endcapture %}
206
+ `);
207
+ const result = processMarkdown(md, '/shared/partials/example.md');
208
+ expect(result.messages).toHaveLength(0);
209
+ });
210
+
211
+ test('allows mix of HTML and Liquid comments', () => {
212
+ const md = dedent(`
213
+ <!-- HTML comment -->
214
+ {% comment %}Liquid comment{% endcomment %}
215
+ {% capture my_var %}Content{% endcapture %}
216
+ <!-- Another HTML comment -->
217
+ `);
218
+ const result = processMarkdown(md, '/shared/partials/example.md');
219
+ expect(result.messages).toHaveLength(0);
220
+ });
221
+ });
222
+
223
+ describe('duplicate capture names', () => {
224
+ test('reports duplicate capture block names', () => {
225
+ const md = dedent(`
226
+ {% capture my_variable %}
227
+ First capture
228
+ {% endcapture %}
229
+
230
+ {% capture my_variable %}
231
+ Duplicate capture
232
+ {% endcapture %}
233
+ `);
234
+ const result = processMarkdown(md, '/shared/partials/example.md');
235
+ expect(result.messages).toHaveLength(1);
236
+ expect(result.messages[0].message).toContain('Duplicate capture block name "my_variable"');
237
+ });
238
+
239
+ test('reports multiple duplicates', () => {
240
+ const md = dedent(`
241
+ {% capture var1 %}First{% endcapture %}
242
+ {% capture var2 %}First{% endcapture %}
243
+ {% capture var1 %}Duplicate 1{% endcapture %}
244
+ {% capture var2 %}Duplicate 2{% endcapture %}
245
+ `);
246
+ const result = processMarkdown(md, '/shared/partials/example.md');
247
+ expect(result.messages).toHaveLength(2);
248
+ expect(result.messages[0].message).toContain('Duplicate capture block name "var1"');
249
+ expect(result.messages[1].message).toContain('Duplicate capture block name "var2"');
250
+ });
251
+
252
+ test('reports all occurrences after the first', () => {
253
+ const md = dedent(`
254
+ {% capture my_var %}First{% endcapture %}
255
+ {% capture my_var %}Second{% endcapture %}
256
+ {% capture my_var %}Third{% endcapture %}
257
+ `);
258
+ const result = processMarkdown(md, '/shared/partials/example.md');
259
+ expect(result.messages).toHaveLength(2);
260
+ });
261
+ });
262
+
263
+ describe('disallowed content', () => {
264
+ test('reports markdown heading outside capture', () => {
265
+ const md = dedent(`
266
+ # This is a heading
267
+
268
+ {% capture my_var %}Content{% endcapture %}
269
+ `);
270
+ const result = processMarkdown(md, '/shared/partials/example.md');
271
+ expect(result.messages.length).toBeGreaterThan(0);
272
+ expect(result.messages[0].message).toContain('must be inside capture blocks');
273
+ });
274
+
275
+ test('reports paragraph content outside capture', () => {
276
+ const md = dedent(`
277
+ {% capture my_var %}Captured{% endcapture %}
278
+
279
+ This is regular paragraph content.
280
+ `);
281
+ const result = processMarkdown(md, '/shared/partials/example.md');
282
+ expect(result.messages.length).toBeGreaterThan(0);
283
+ expect(result.messages[0].message).toContain('must be inside capture blocks');
284
+ });
285
+
286
+ test('reports list outside capture', () => {
287
+ const md = dedent(`
288
+ - List item 1
289
+ - List item 2
290
+
291
+ {% capture my_var %}Captured{% endcapture %}
292
+ `);
293
+ const result = processMarkdown(md, '/shared/partials/example.md');
294
+ expect(result.messages.length).toBeGreaterThan(0);
295
+ expect(result.messages[0].message).toContain('must be inside capture blocks');
296
+ });
297
+
298
+ test('reports code block outside capture', () => {
299
+ const md = dedent(`
300
+ \`\`\`js
301
+ const x = 1;
302
+ \`\`\`
303
+
304
+ {% capture my_var %}Captured{% endcapture %}
305
+ `);
306
+ const result = processMarkdown(md, '/shared/partials/example.md');
307
+ expect(result.messages.length).toBeGreaterThan(0);
308
+ expect(result.messages[0].message).toContain('must be inside capture blocks');
309
+ });
310
+
311
+ test('allows whitespace outside capture blocks', () => {
312
+ const md = dedent(`
313
+
314
+
315
+ {% capture my_var %}Content{% endcapture %}
316
+
317
+
318
+ {% capture another_var %}More content{% endcapture %}
319
+
320
+
321
+ `);
322
+ const result = processMarkdown(md, '/shared/partials/example.md');
323
+ expect(result.messages).toHaveLength(0);
324
+ });
325
+ });
326
+
327
+ describe('complex scenarios', () => {
328
+ test('valid file with multiple captures and comments', () => {
329
+ const md = dedent(`
330
+ <!-- Header comment explaining this file -->
331
+
332
+ {% comment %}
333
+ This section defines variable 1
334
+ {% endcomment %}
335
+ {% capture variable_one %}
336
+ # Section 1
337
+
338
+ Content for variable one.
339
+ {% endcapture %}
340
+
341
+ <!-- Separator comment -->
342
+
343
+ {% capture variable_two %}
344
+ ## Section 2
345
+
346
+ Content for variable two with **formatting**.
347
+ {% endcapture %}
348
+
349
+ {%- comment -%}End of file{%- endcomment -%}
350
+ `);
351
+ const result = processMarkdown(md, '/shared/partials/example.md');
352
+ expect(result.messages).toHaveLength(0);
353
+ });
354
+
355
+ test('reports both duplicate names and disallowed content', () => {
356
+ const md = dedent(`
357
+ # Invalid heading
358
+
359
+ {% capture my_var %}First{% endcapture %}
360
+ {% capture my_var %}Duplicate{% endcapture %}
361
+ `);
362
+ const result = processMarkdown(md, '/shared/partials/example.md');
363
+ expect(result.messages.length).toBeGreaterThan(0);
364
+ // Should have both error types
365
+ const messages = result.messages.map(m => m.message);
366
+ expect(messages.some(m => m.includes('Duplicate capture'))).toBe(true);
367
+ expect(messages.some(m => m.includes('must be inside capture blocks'))).toBe(true);
368
+ });
369
+
370
+ test('handles nested liquid tags inside capture', () => {
371
+ const md = dedent(`
372
+ {% capture my_content %}
373
+ This has {% if true %}conditional{% endif %} content.
374
+ And a {{ variable }} reference.
375
+ {% endcapture %}
376
+ `);
377
+ const result = processMarkdown(md, '/shared/partials/example.md');
378
+ expect(result.messages).toHaveLength(0);
379
+ });
380
+ });
381
+
382
+ describe('edge cases', () => {
383
+ test('handles empty file', () => {
384
+ const md = '';
385
+ const result = processMarkdown(md, '/shared/partials/example.md');
386
+ expect(result.messages).toHaveLength(0);
387
+ });
388
+
389
+ test('handles file with only comments', () => {
390
+ const md = dedent(`
391
+ <!-- Only comments here -->
392
+ {% comment %}No capture blocks{% endcomment %}
393
+ `);
394
+ const result = processMarkdown(md, '/shared/partials/example.md');
395
+ expect(result.messages).toHaveLength(0);
396
+ });
397
+
398
+ test('handles file with only whitespace', () => {
399
+ const md = ' \n\n\t\n ';
400
+ const result = processMarkdown(md, '/shared/partials/example.md');
401
+ expect(result.messages).toHaveLength(0);
402
+ });
403
+
404
+ test('handles capture block with underscores and numbers in name', () => {
405
+ const md = dedent(`
406
+ {% capture my_var_123 %}Content{% endcapture %}
407
+ `);
408
+ const result = processMarkdown(md, '/shared/partials/example.md');
409
+ expect(result.messages).toHaveLength(0);
410
+ });
411
+
412
+ test('handles missing file path gracefully', () => {
413
+ const file = vfile({ contents: '{% capture x %}test{% endcapture %}' });
414
+ const result = unified()
415
+ .use(markdown)
416
+ .use(plugin as unified.Attacher)
417
+ .use(stringify)
418
+ .processSync(file);
419
+ expect(result.messages).toHaveLength(0);
420
+ });
421
+
422
+ test('validates various path formats for partials', () => {
423
+ const md = '{% capture x %}Content{% endcapture %}';
424
+
425
+ // These should all be treated as partials (valid) - must contain '/shared/partials/'
426
+ expect(processMarkdown(md, '/app/shared/partials/x.md').messages).toHaveLength(0);
427
+ expect(processMarkdown(md, '/shared/partials/x.md').messages).toHaveLength(0);
428
+ expect(processMarkdown(md, '/content/shared/partials/alerts.md').messages).toHaveLength(0);
429
+ expect(processMarkdown(md, 'docs/shared/partials/alerts.md').messages).toHaveLength(0);
430
+ });
431
+
432
+ test('validates various path formats for non-partials', () => {
433
+ const md = '{% capture x %}Content{% endcapture %}';
434
+
435
+ // These should all be treated as non-partials (invalid)
436
+ expect(processMarkdown(md, '/docs/guides/intro.md').messages).toHaveLength(1);
437
+ expect(processMarkdown(md, 'content/tutorials/example.md').messages).toHaveLength(1);
438
+ expect(processMarkdown(md, '/shared/snippets/code.md').messages).toHaveLength(1);
439
+ expect(processMarkdown(md, 'README.md').messages).toHaveLength(1);
440
+ });
441
+
442
+ test('does not match similar but different paths', () => {
443
+ const md = '# Heading';
444
+
445
+ // These paths should NOT be treated as partials
446
+ expect(processMarkdown(md, '/shared-partials/x.md').messages).toHaveLength(0);
447
+ expect(processMarkdown(md, '/sharedpartials/x.md').messages).toHaveLength(0);
448
+ expect(processMarkdown(md, '/my-shared/partials/x.md').messages).toHaveLength(0);
449
+ });
450
+ });
451
+ });
package/src/index.ts ADDED
@@ -0,0 +1,135 @@
1
+ /**
2
+ * For .md files in shared/partials folder,
3
+ * all content must be in capture blocks
4
+ * only HTML and Liquid comments are allowed outside capture blocks
5
+ * we should not have duplicate names for capture blocks
6
+ *
7
+ * For .md files outside shared/partials folder, a file must not contain capture block
8
+ */
9
+
10
+ import rule from 'unified-lint-rule';
11
+ import { VFile } from 'vfile';
12
+ import { offsetToPosition } from '@salesforcedevs/sfdocs-liquid-lint-utils';
13
+
14
+ const SOURCE = 'sfdocs-liquid-lint:liquid-capture';
15
+
16
+ // Regex patterns
17
+ const CAPTURE_BLOCK_RE = /{%-?\s*capture\s+(\w+)\s*-?%}([\s\S]*?){%-?\s*endcapture\s*-?%}/g;
18
+ const HTML_COMMENT_RE = /<!--[\s\S]*?-->/g;
19
+ const LIQUID_COMMENT_RE = /{%-?\s*comment\s*-?%}[\s\S]*?{%-?\s*endcomment\s*-?%}/g;
20
+
21
+ interface CaptureBlock {
22
+ name: string;
23
+ startOffset: number;
24
+ endOffset: number;
25
+ }
26
+
27
+ function isInPartialsFolder(filePath: string): boolean {
28
+ return filePath.includes('/shared/partials/');
29
+ }
30
+
31
+ function extractCaptureBlocks(content: string): CaptureBlock[] {
32
+ const captures: CaptureBlock[] = [];
33
+ const re = new RegExp(CAPTURE_BLOCK_RE.source, 'g');
34
+ let match: RegExpExecArray | null;
35
+
36
+ while ((match = re.exec(content)) !== null) {
37
+ captures.push({
38
+ name: match[1],
39
+ startOffset: match.index,
40
+ endOffset: match.index + match[0].length,
41
+ });
42
+ }
43
+
44
+ return captures;
45
+ }
46
+
47
+ function findDuplicateCaptures(captures: CaptureBlock[]): Map<string, CaptureBlock[]> {
48
+ const nameMap = new Map<string, CaptureBlock[]>();
49
+
50
+ for (const capture of captures) {
51
+ if (!nameMap.has(capture.name)) {
52
+ nameMap.set(capture.name, []);
53
+ }
54
+ nameMap.get(capture.name)!.push(capture);
55
+ }
56
+
57
+ // Filter to only duplicates
58
+ const duplicates = new Map<string, CaptureBlock[]>();
59
+ for (const [name, blocks] of nameMap.entries()) {
60
+ if (blocks.length > 1) {
61
+ duplicates.set(name, blocks);
62
+ }
63
+ }
64
+
65
+ return duplicates;
66
+ }
67
+
68
+ function removeAllowedContent(content: string): string {
69
+ let cleaned = content;
70
+
71
+ cleaned = cleaned.replace(CAPTURE_BLOCK_RE, '');
72
+
73
+ cleaned = cleaned.replace(HTML_COMMENT_RE, '');
74
+
75
+ cleaned = cleaned.replace(LIQUID_COMMENT_RE, '');
76
+
77
+ return cleaned;
78
+ }
79
+
80
+ function hasDisallowedContent(cleaned: string): boolean {
81
+ return /\S/.test(cleaned);
82
+ }
83
+
84
+ function checkLiquidCapture(_tree: unknown, file: VFile): void {
85
+ if (!file.path) return;
86
+
87
+ const raw = String(file.contents ?? '');
88
+ if (!raw) return;
89
+
90
+ const captureBlocks = extractCaptureBlocks(raw);
91
+
92
+ if (!isInPartialsFolder(file.path)) {
93
+ if (captureBlocks.length > 0) {
94
+ for (const capture of captureBlocks) {
95
+ const position = offsetToPosition(raw, capture.startOffset);
96
+ file.message(
97
+ `Capture blocks are not allowed in files outside shared/partials folder. Found capture block "${capture.name}".`,
98
+ position
99
+ );
100
+ }
101
+ }
102
+ return;
103
+ }
104
+
105
+ const duplicates = findDuplicateCaptures(captureBlocks);
106
+ if (duplicates.size > 0) {
107
+ for (const [name, blocks] of duplicates.entries()) {
108
+ // Report on all occurrences after the first
109
+ for (let i = 1; i < blocks.length; i++) {
110
+ const position = offsetToPosition(raw, blocks[i].startOffset);
111
+ file.message(
112
+ `Duplicate capture block name "${name}". This variable already exists in file.`,
113
+ position
114
+ );
115
+ }
116
+ }
117
+ }
118
+
119
+ // Check for disallowed content outside capture blocks and comments
120
+ const cleaned = removeAllowedContent(raw);
121
+ if (hasDisallowedContent(cleaned)) {
122
+ // Find the first line with disallowed content for better error reporting
123
+ const lines = cleaned.split('\n');
124
+ for (const line of lines) {
125
+ if (/\S/.test(line)) {
126
+ file.message(
127
+ `Content in shared/partials files must be inside capture blocks. Found content outside capture blocks: "${line.trim().substring(0, 50)}..."`
128
+ );
129
+ break;
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ export = rule(SOURCE, checkLiquidCapture);
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.settings.json",
3
+ "moduleResolution": "node",
4
+ "compilerOptions": {
5
+ "rootDir": "src",
6
+ "outDir": "dist"
7
+ }
8
+ }
@@ -0,0 +1 @@
1
+ {"program":{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/@types/unist/index.d.ts","../../node_modules/vfile-message/types/index.d.ts","../../node_modules/vfile/types/index.d.ts","../liquid-lint-utils/dist/parse-frontmatter.d.ts","../liquid-lint-utils/dist/disabled-tags.d.ts","../liquid-lint-utils/dist/is-liquid-enabled.d.ts","../liquid-lint-utils/dist/offset-to-position.d.ts","../liquid-lint-utils/dist/protect-code-blocks.d.ts","../liquid-lint-utils/dist/resolve-roots.d.ts","../liquid-lint-utils/dist/index.d.ts","./src/index.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostic_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/util/types.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/globals.global.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/ts3.6/base.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/base.d.ts","./node_modules/@types/node/index.d.ts","../../node_modules/@babel/types/lib/index.d.ts","../../node_modules/@types/babel__generator/index.d.ts","../../node_modules/@babel/parser/typings/babel-parser.d.ts","../../node_modules/@types/babel__template/index.d.ts","../../node_modules/@types/babel__traverse/index.d.ts","../../node_modules/@types/babel__core/index.d.ts","../../node_modules/keyv/src/index.d.ts","../../node_modules/@types/http-cache-semantics/index.d.ts","../../node_modules/@types/responselike/index.d.ts","../../node_modules/@types/cacheable-request/index.d.ts","../../node_modules/@types/dedent/index.d.ts","../../node_modules/@types/graceful-fs/index.d.ts","../../node_modules/@types/istanbul-lib-coverage/index.d.ts","../../node_modules/@types/istanbul-lib-report/index.d.ts","../../node_modules/@types/istanbul-reports/index.d.ts","../../node_modules/@types/jest/node_modules/jest-diff/build/cleanupsemantic.d.ts","../../node_modules/@types/jest/node_modules/jest-diff/build/types.d.ts","../../node_modules/@types/jest/node_modules/jest-diff/build/difflines.d.ts","../../node_modules/@types/jest/node_modules/jest-diff/build/printdiffs.d.ts","../../node_modules/@types/jest/node_modules/jest-diff/build/index.d.ts","../../node_modules/@types/jest/node_modules/pretty-format/build/types.d.ts","../../node_modules/@types/jest/node_modules/pretty-format/build/index.d.ts","../../node_modules/@types/jest/index.d.ts","../../node_modules/@types/jest/ts3.2/index.d.ts","../../node_modules/@types/js-yaml/index.d.ts","../../node_modules/@types/json-schema/index.d.ts","../../node_modules/@types/jsonpath-plus/index.d.ts","../../node_modules/@types/keyv/index.d.ts","../../node_modules/@types/mdast/index.d.ts","../../node_modules/@types/minimatch/index.d.ts","../../node_modules/@types/minimist/index.d.ts","../../node_modules/@types/mock-fs/lib/item.d.ts","../../node_modules/@types/mock-fs/lib/file.d.ts","../../node_modules/@types/mock-fs/lib/directory.d.ts","../../node_modules/@types/mock-fs/lib/symlink.d.ts","../../node_modules/@types/mock-fs/lib/filesystem.d.ts","../../node_modules/@types/mock-fs/index.d.ts","../../node_modules/@types/normalize-package-data/index.d.ts","../../node_modules/@types/prettier/index.d.ts","../../node_modules/@types/stack-utils/index.d.ts","../../node_modules/@types/yargs-parser/index.d.ts","../../node_modules/@types/yargs/index.d.ts"],"fileInfos":[{"version":"8730f4bf322026ff5229336391a18bcaa1f94d4f82416c8b2f3954e2ccaae2ba","affectsGlobalScope":true},"dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6","7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467","8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9","5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06","4b421cbfb3a38a27c279dec1e9112c3d1da296f77a1a85ddadf7e7a425d45d18",{"version":"adb996790133eb33b33aadb9c09f15c2c575e71fb57a62de8bf74dbf59ec7dfb","affectsGlobalScope":true},{"version":"8cc8c5a3bac513368b0157f3d8b31cfdcfe78b56d3724f30f80ed9715e404af8","affectsGlobalScope":true},{"version":"cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a","affectsGlobalScope":true},{"version":"c5c05907c02476e4bde6b7e76a79ffcd948aedd14b6a8f56e4674221b0417398","affectsGlobalScope":true},{"version":"5f406584aef28a331c36523df688ca3650288d14f39c5d2e555c95f0d2ff8f6f","affectsGlobalScope":true},{"version":"22f230e544b35349cfb3bd9110b6ef37b41c6d6c43c3314a31bd0d9652fcec72","affectsGlobalScope":true},{"version":"7ea0b55f6b315cf9ac2ad622b0a7813315bb6e97bf4bb3fbf8f8affbca7dc695","affectsGlobalScope":true},{"version":"3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93","affectsGlobalScope":true},{"version":"eb26de841c52236d8222f87e9e6a235332e0788af8c87a71e9e210314300410a","affectsGlobalScope":true},{"version":"3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006","affectsGlobalScope":true},{"version":"17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a","affectsGlobalScope":true},{"version":"7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98","affectsGlobalScope":true},{"version":"6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577","affectsGlobalScope":true},{"version":"81cac4cbc92c0c839c70f8ffb94eb61e2d32dc1c3cf6d95844ca099463cf37ea","affectsGlobalScope":true},{"version":"b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e","affectsGlobalScope":true},{"version":"0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a","affectsGlobalScope":true},{"version":"da233fc1c8a377ba9e0bed690a73c290d843c2c3d23a7bd7ec5cd3d7d73ba1e0","affectsGlobalScope":true},{"version":"d154ea5bb7f7f9001ed9153e876b2d5b8f5c2bb9ec02b3ae0d239ec769f1f2ae","affectsGlobalScope":true},{"version":"bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c","affectsGlobalScope":true},{"version":"c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8","affectsGlobalScope":true},{"version":"9d57b2b5d15838ed094aa9ff1299eecef40b190722eb619bac4616657a05f951","affectsGlobalScope":true},{"version":"6c51b5dd26a2c31dbf37f00cfc32b2aa6a92e19c995aefb5b97a3a64f1ac99de","affectsGlobalScope":true},{"version":"6e7997ef61de3132e4d4b2250e75343f487903ddf5370e7ce33cf1b9db9a63ed","affectsGlobalScope":true},{"version":"2ad234885a4240522efccd77de6c7d99eecf9b4de0914adb9a35c0c22433f993","affectsGlobalScope":true},{"version":"5e5e095c4470c8bab227dbbc61374878ecead104c74ab9960d3adcccfee23205","affectsGlobalScope":true},{"version":"09aa50414b80c023553090e2f53827f007a301bc34b0495bfb2c3c08ab9ad1eb","affectsGlobalScope":true},{"version":"2768ef564cfc0689a1b76106c421a2909bdff0acbe87da010785adab80efdd5c","affectsGlobalScope":true},{"version":"52d1bb7ab7a3306fd0375c8bff560feed26ed676a5b0457fa8027b563aecb9a4","affectsGlobalScope":true},"6d09838b65c3c780513878793fc394ae29b8595d9e4729246d14ce69abc71140","eb09cf44043f7d6e0208dca6f0555207015e91fff5ff77b9c21d63672f7d68d5","bbf6f246061b92bb84241897eebfcdb9ce28444ab6acbc32c425388dd27c1011","55fb7da9a36a3d45502aa14b33c55e4f3baa96567978c189a7d6e4dbbfe6cd2d","36300e1494e25976af66784b85d7bf45afd4d5c71162db0350a603ae0e9e20c8","d4ffd5feb926ecddba6ff81487d9c7d6f4c759620f5145ceea6189671b6fe25d","a08c13d8c556d1c14bcd59d91e87b7e4693979db65c600ff33a069d4aa2df53e","3078a8fa4448f802b3a522b8170be54b14bf3563ac8904504a3304777b1540ff","9860ced5ed4a2a5cd4b994721d82efdf0ec1ae8b6c722a7aa95b0087ac3732aa","9811cff809a704fa631574fcce9c1a0803fb4d3c448997c44c71974b2f2a828c",{"version":"f4c5a4acf2d5301f5d6fe1aa5f2b0fc95a7d07b41832f5975a3228c912e78a9b","signature":"f7d3734419310acc0d81e9ff6dfdec2afdf4910c0c46656b45f5638286b89824"},"c7bdc99177a2a94d25fb13722adaaf5b3291bf70b4d1b27584ba189dd3889ba3",{"version":"d1c92b66c4105659fcad4eb76a1481f7311033e117d0678a1ec545e8ddb8d4c6","affectsGlobalScope":true},"e23424b97418eca3226fd24de079f1203eb70360622e4e093af2aff14d4be6ec","dee93c07b4df5e26010dc9ec4cdf4d6e4076bb4474d2a8371529217c8b2740a4","ed40f2f184db052dc8df62d1f199823c8bbccc487c0a2a7c54eeea0badcf4378","04eaa93bd75f937f9184dcb95a7983800c5770cf8ddd8ac0f3734dc02f5b20ef",{"version":"c8155caf28fc7b0a564156a5df28ad8a844a3bd32d331d148d8f3ce88025c870","affectsGlobalScope":true},"45ac321f2e15d268fd74a90ddaa6467dcaaff2c5b13f95b4b85831520fb7a491","dfc747ab8dd5f623055a4c26fd35e8cceca869fd3f1cf09701c941ca3679665a","c9f5f2920ff61d7158417b8440d5181ddc34a3dfef811a5677dd8a9fb91471e9","5cc0a492da3602510b8f5ed1852b1e0390002780d8758fbc8c0cd023ca7085f8","ec7dafafe751a5121f8f1c80201ebe7e7238c47e6329280a73c4d1ca4bb7fa28","64debeb10e4b7ae4ec9e89bfb4e04c6101ab98c3cc806d14e5488607cfec2753",{"version":"2866a528b2708aa272ec3eaafd3c980abb23aec1ef831cfc5eb2186b98c37ce5","affectsGlobalScope":true},{"version":"a5782d6cea81fe43d2db7ed41e789458c933ab3ab60602f7b5b14c4da3370496","affectsGlobalScope":true},"f258ba66915f0196ec344bc53afe1888240bbcc855ebd151b6cc072275533319","6194335ee3353f7226ba31f31d6301d0c6be87228419c0a08976ccd9d4f1213e","3ac12a54cfaa84683506db8d4cf779135a271d9f0ec18930cf53e61fbeea0c5d","cf3d3b087d1a8a3355eec47d206162c75e912315b9b5c1cd49fda93e948fb80a","36f316c066c4a72dd6f19fec49a074f935744fc9ccbe75c87ebc07fb2feb9062","42176966283d3835c34278b9b5c0f470d484c0c0c6a55c20a2c916a1ce69b6e8","0cff7901aedfe78e314f7d44088f07e2afa1b6e4f0473a4169b8456ca2fb245d","ec70fd6f8a9a83f850dab2960a6789e93d5b034b354a16814cad5daabf62a360","e2236264a811ed1d09a2487a433e8f5216ae62378cf233954ae220cf886f6717","3ec1e108d587a5661ec790db607f482605ba9f3830e118ce578e3ffa3c42e22b","100b3bb9d39d2b1b5340f1bf45a52e94ef1692b45232b4ba00fac5c3cc56d331",{"version":"04fe7e7d8008887943260af1fde2bfd4877e0dc57bf634e0f0b2f3d1794dfd11","affectsGlobalScope":true},"7f77304372efe3c9967e5f9ea2061f1b4bf41dc3cda3c83cdd676f2e5af6b7e6","992c6f6be16c0a1d2eec13ece33adeea2c747ba27fcd078353a8f4bb5b4fea58","2597718d91e306949d89e285bf34c44192014ef541c3bd7cbb825c022749e974","a6b0abdb67d63ebe964ba5fee31bc3daf10c12eecd46b24d778426010c04c67e","ac4801ebc2355ba32329070123b1cd15891bf71b41dcaf9e75b4744832126a59","fd2298fba0640e7295e7bd545e2dfbfcccbb00c27019e501c87965a02bbdebf6","4fd3c4debadce3e9ab9dec3eb45f7f5e2e3d4ad65cf975a6d938d883cfb25a50","71ddd49185b68f27bfac127ef5d22cb2672c278e53e5370d9020ef50ca9c377d","b1ea7a6eaa7608e0e0529aebd323b526a79c6c05a4e519ae5c779675004dcdf1","9fcb033a6208485d8f3fadde31eb5cbcaf99149cff3e40c0dc53ebc6d0dff4e9","7df562288f949945cf69c21cd912100c2afedeeb7cdb219085f7f4b46cb7dde4","9d16690485ff1eb4f6fc57aebe237728fd8e03130c460919da3a35f4d9bd97f5","dcc6910d95a3625fd2b0487fda055988e46ab46c357a1b3618c27b4a8dd739c9","f4149f1aa299474c7040a35fe8f8ac2ad078cc1b190415adc1fff3ed52d490ea","3730099ed008776216268a97771de31753ef71e0a7d0ec650f588cba2a06ce44","8d649dbc429d7139a1d9a14ea2bf8af1dc089e0a879447539587463d4b6c248c","60c9e27816ec8ac8df7240598bb086e95b47edfb454c5cbf4003c812e0ed6e39","e361aecf17fc4034b4c122a1564471cdcd22ef3a51407803cb5a5fc020c04d02","4926467de88a92a4fc9971d8c6f21b91eca1c0e7fc2a46cc4638ab9440c73875",{"version":"2708349d5a11a5c2e5f3a0765259ebe7ee00cdcc8161cb9990cb4910328442a1","affectsGlobalScope":true},"fc0ae4a8ad3c762b96f9d2c3700cb879a373458cb0bf3175478e3b4f85f7ef2f","fabbec378e1ddd86fcf2662e716c2b8318acedb664ee3a4cba6f9e8ee8269cf1","b3593bd345ebea5e4d0a894c03251a3774b34df3d6db57075c18e089a599ba76","e61a21e9418f279bc480394a94d1581b2dee73747adcbdef999b6737e34d721b","efd55e8ca79171bf26c0d0e30221cb8ee1f5a31dd0c791ec4b2e886f42bab124","c2c2a861a338244d7dd700d0c52a78916b4bb75b98fc8ca5e7c501899fc03796","b6d03c9cfe2cf0ba4c673c209fcd7c46c815b2619fd2aad59fc4229aaef2ed43","adb467429462e3891de5bb4a82a4189b92005d61c7f9367c089baf03997c104e","670a76db379b27c8ff42f1ba927828a22862e2ab0b0908e38b671f0e912cc5ed","13b77ab19ef7aadd86a1e54f2f08ea23a6d74e102909e3c00d31f231ed040f62","069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9","42baf4ca38c38deaf411ea73f37bc39ff56c6e5c761a968b64ac1b25c92b5cd8","d7dbe0ad36bdca8a6ecf143422a48e72cc8927bab7b23a1a2485c2f78a7022c6","8718fa41d7cf4aa91de4e8f164c90f88e0bf343aa92a1b9b725a9c675c64e16b","f992cd6cc0bcbaa4e6c810468c90f2d8595f8c6c3cf050c806397d3de8585562","3189093b3543e545bcf0e6d387d60c5cfe5a224b104d15b1639c0c4db9ecf012","afe73051ff6a03a9565cbd8ebb0e956ee3df5e913ad5c1ded64218aabfa3dcb5","035a5df183489c2e22f3cf59fc1ed2b043d27f357eecc0eb8d8e840059d44245","a4809f4d92317535e6b22b01019437030077a76fec1d93b9881c9ed4738fcc54","5f53fa0bd22096d2a78533f94e02c899143b8f0f9891a46965294ee8b91a9434","e222104af6cb9415238ad358488b74d76eceeff238c1268ec6e85655b05341da","69da61a7b5093dac77fa3bec8be95dcf9a74c95a0e9161edb98bb24e30e439d2","eba230221317c985ab1953ccc3edc517f248b37db4fef7875cb2c8d08aff7be7","b83e796810e475da3564c6515bc0ae9577070596a33d89299b7d99f94ecfd921","b4439890c168d646357928431100daac5cbdee1d345a34e6bf6eca9f3abe22bc","5d72971a459517c44c1379dab9ed248e87a61ba0a1e0f25c9d67e1e640cd9a09","02d734976af36f4273d930bea88b3e62adf6b078cf120c1c63d49aa8d8427c5c",{"version":"f624e578325b8c58e55b30c998b1f4c3ec1b61a9fa66373da4250c89b7880d44","affectsGlobalScope":true},{"version":"d3002f620eab4bf6476c9da5c0efb2041d46f7df8b3032a5631bd206abef2c75","affectsGlobalScope":true},"7a1dd1e9c8bf5e23129495b10718b280340c7500570e0cfe5cffcdee51e13e48","f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","9bd81cfc874a2d896326e6d9e7a6963677389a8269b1d032663083b253e21162","fec943fdb3275eb6e006b35e04a8e2e99e9adf3f4b969ddf15315ac7575a93e4","202f8582ee3cd89e06c4a17d8aabb925ff8550370559c771d1cc3ec3934071c2","8841e2aa774b89bd23302dede20663306dc1b9902431ac64b24be8b8d0e3f649","fbca5ffaebf282ec3cdac47b0d1d4a138a8b0bb32105251a38acb235087d3318","b95f2a78de34a873c6dd76dc538b7a5fec77da6a0e0e7efc7aa58f58ddfce270","4b50f58fcf29daaeab0c583da58ee9731a4d5c003f99fd833d91ad99f19a82c3","6692416887d66b903aea54b32163d34318300772cd2b8c7c36f972937a62de74","3e640a0056177a4ccfb819d81b21c9ed0ac0e77d8b7bf580894a3392298a890b","b75cb207f8dfade4120ba3554c5781005c9de85723c76588befd47693342ca50","693c4c9acf5e1c56e30130a90e07bd05122e6194d69018b0b20c6581c1324e0a","22293bd6fa12747929f8dfca3ec1684a3fe08638aa18023dd286ab337e88a592","d88a5e779faf033be3d52142a04fbe1cb96009868e3bbdd296b2bc6c59e06c0e","ab82804a14454734010dcdcd43f564ff7b0389bee4c5692eec76ff5b30d4cf66","bae8d023ef6b23df7da26f51cea44321f95817c190342a36882e93b80d07a960","ae271d475b632ce7b03fea6d9cf6da72439e57a109672671cbc79f54e1386938"],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"experimentalDecorators":true,"module":1,"noUnusedLocals":true,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":false,"target":6},"fileIdsList":[[98],[98,99,100,101,102],[98,100],[60,63,85,97,104,105,106],[61,97],[110],[111],[117,119],[113,114],[113,114,115,116],[118],[120],[60,97],[35],[130,131,132,133],[129],[97,129],[129,130,131,132],[63,77,97],[138],[60],[35,36],[95],[94,95],[49,54],[60,61,68,77],[50,60,68],[86],[54,61,69],[77,82],[57,60,68],[58],[57],[60,62,77,85],[60,61,77],[68,77,85],[60,61,63,68,77,82,85],[63,77,82,85],[96],[85],[57,60,77],[70],[48],[84],[75,86,89],[60,78],[77],[80],[54,68],[46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93],[68],[74],[87],[49,54,60,62,71,77,85,89],[37,44],[38,39,40,41,42,43]],"referencedMap":[[100,1],[103,2],[99,1],[101,3],[102,1],[107,4],[109,5],[111,6],[112,7],[120,8],[115,9],[117,10],[116,9],[119,11],[121,12],[125,13],[126,14],[134,15],[131,16],[130,17],[133,18],[132,16],[106,19],[139,20],[104,21],[36,14],[37,22],[46,23],[96,24],[49,25],[50,26],[51,27],[52,28],[53,29],[54,30],[55,31],[57,32],[58,33],[59,21],[60,21],[61,34],[62,35],[63,36],[64,37],[65,38],[97,39],[66,21],[67,40],[68,41],[70,42],[71,43],[72,44],[75,21],[76,45],[77,46],[78,47],[80,21],[81,48],[82,49],[94,50],[84,51],[85,52],[86,53],[88,47],[90,54],[91,47],[45,55],[44,56]],"exportedModulesMap":[[100,1],[103,2],[99,1],[101,3],[102,1],[107,4],[109,5],[111,6],[112,7],[120,8],[115,9],[117,10],[116,9],[119,11],[121,12],[125,13],[126,14],[134,15],[131,16],[130,17],[133,18],[132,16],[106,19],[139,20],[104,21],[36,14],[37,22],[46,23],[96,24],[49,25],[50,26],[51,27],[52,28],[53,29],[54,30],[55,31],[57,32],[58,33],[59,21],[60,21],[61,34],[62,35],[63,36],[64,37],[65,38],[97,39],[66,21],[67,40],[68,41],[70,42],[71,43],[72,44],[75,21],[76,45],[77,46],[78,47],[80,21],[81,48],[82,49],[94,50],[84,51],[85,52],[86,53],[88,47],[90,54],[91,47],[44,56]],"semanticDiagnosticsPerFile":[100,98,103,99,101,102,107,108,109,105,110,111,112,120,113,115,117,116,114,119,118,121,122,123,124,125,126,127,128,134,131,130,133,129,132,135,136,106,137,35,138,139,104,8,7,2,9,10,11,12,13,14,15,16,3,4,20,17,18,19,21,22,23,5,24,25,26,27,6,31,28,29,30,32,33,1,34,36,37,95,46,48,96,49,50,51,52,53,54,55,56,57,58,59,60,61,62,47,92,63,64,65,97,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,94,84,85,86,87,88,89,93,90,91,45,39,44,40,41,38,42,43],"latestChangedDtsFile":"./dist/index.d.ts"},"version":"4.9.5"}