@boundaries/elements 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Javier Brea
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,568 @@
1
+ [![Build status][build-image]][build-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Quality Gate][quality-gate-image]][quality-gate-url]
2
+
3
+ [![Renovate](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://renovatebot.com) [![Last commit][last-commit-image]][last-commit-url] [![Last release][release-image]][release-url]
4
+
5
+ [![NPM downloads][npm-downloads-image]][npm-downloads-url] [![License][license-image]][license-url]
6
+
7
+ [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fjavierbrea%2Feslint-plugin-boundaries%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/javierbrea/eslint-plugin-boundaries/master)
8
+
9
+ # @boundaries/elements
10
+
11
+ > Element descriptors and matchers for enforcing architectural boundaries in JavaScript/TypeScript projects
12
+
13
+ ## Table of Contents
14
+
15
+ - [Introduction](#introduction)
16
+ - [Features](#features)
17
+ - [Installation](#installation)
18
+ - [Quick Start](#quick-start)
19
+ - [Usage](#usage)
20
+ - [Configuration Options](#configuration-options)
21
+ - [Creating a matcher](#creating-a-matcher)
22
+ - [Element Descriptors](#element-descriptors)
23
+ - [Selectors](#selectors)
24
+ - [Template Variables](#template-variables)
25
+ - [Using Matchers](#using-matchers)
26
+ - [Element Matching](#element-matching)
27
+ - [Dependency Matching](#dependency-matching)
28
+ - [API Reference](#api-reference)
29
+ - [Legacy Selectors](#legacy-selectors)
30
+ - [Contributing](#contributing)
31
+ - [License](#license)
32
+
33
+ ## Introduction
34
+
35
+ `@boundaries/elements` provides a powerful and flexible system for defining and enforcing architectural boundaries in your JavaScript and TypeScript projects. It allows you to:
36
+
37
+ - **Define element types** based on file path patterns (e.g., components, services, helpers)
38
+ - **Match elements** against specific criteria
39
+ - **Validate dependencies** between different parts of your codebase
40
+ - **Enforce architectural rules** by checking if dependencies between elements are allowed
41
+
42
+ This package is part of the [@boundaries ecosystem](https://github.com/javierbrea/eslint-plugin-boundaries) and uses [Micromatch patterns](https://github.com/micromatch/micromatch) for flexible and powerful pattern matching.
43
+
44
+ >[!NOTE]
45
+ > This package does not read or analyze your codebase directly. It provides the core logic for defining and matching elements and dependencies, which can be integrated into other tools such as linters or build systems.
46
+
47
+ ## Features
48
+
49
+ - ✨ **Flexible pattern matching** using Micromatch syntax
50
+ - 🎯 **Element type and category identification** based on file paths
51
+ - 📝 **Template variables** for dynamic selector matching
52
+ - ⚡ **Built-in caching** for optimal performance
53
+ - 🔄 **Support for multiple file matching modes** (file, folder, full path)
54
+ - 🎨 **Capture path fragments** for advanced matching scenarios
55
+
56
+ ## Installation
57
+
58
+ Install the package via npm:
59
+
60
+ ```bash
61
+ npm install @boundaries/elements
62
+ ```
63
+
64
+ ## Quick Start
65
+
66
+ Here's a quick example to get you started:
67
+
68
+ ```typescript
69
+ import { Elements } from '@boundaries/elements';
70
+
71
+ // Create an Elements instance
72
+ const elements = new Elements();
73
+
74
+ // Define element descriptors
75
+ const matcher = elements.getMatcher([
76
+ {
77
+ type: "component",
78
+ category: "react",
79
+ pattern: "src/components/*.tsx",
80
+ mode: "file",
81
+ capture: ["fileName"],
82
+ },
83
+ {
84
+ type: "service",
85
+ category: "data",
86
+ pattern: "src/services/*.ts",
87
+ mode: "file",
88
+ capture: ["fileName"],
89
+ },
90
+ ]);
91
+
92
+ // Match an element
93
+ const isComponent = matcher.isMatch("src/components/Button.tsx", {
94
+ type: "component"
95
+ }); // true
96
+
97
+ // Match a dependency
98
+ const isValidDependency = matcher.isMatch(
99
+ {
100
+ from: "src/components/Button.tsx",
101
+ to: "src/services/Api.ts",
102
+ source: "../services/Api",
103
+ kind: "value",
104
+ nodeKind: "ImportDeclaration",
105
+ },
106
+ {
107
+ from: { category: "react" },
108
+ to: { type: "service" },
109
+ }
110
+ ); // true
111
+ ```
112
+
113
+ ## Usage
114
+
115
+ ### Configuration Options
116
+
117
+ When creating an `Elements` instance, you can provide configuration options that will be used as defaults for all matchers:
118
+
119
+ ```typescript
120
+ const elements = new Elements({
121
+ ignorePaths: ["**/dist/**", "**/build/**", "**/node_modules/**"],
122
+ includePaths: ["src/**/*"],
123
+ });
124
+ ```
125
+
126
+ **Available options:**
127
+
128
+ - **`ignorePaths`**: Micromatch pattern(s) to exclude certain paths from element matching (default: none)
129
+ - **`includePaths`**: Micromatch pattern(s) to include only specific paths (default: all paths)
130
+ - **`legacyTemplates`**: Whether to enable legacy template syntax support (default: `true`, but it will be `false` in future releases). This allows using `${variable}` syntax in templates for backward compatibility.
131
+
132
+ ### Creating a Matcher
133
+
134
+ Use the `getMatcher` method to create a matcher with element descriptors:
135
+
136
+ ```typescript
137
+ const matcher = elements.getMatcher([
138
+ {
139
+ type: "component",
140
+ pattern: "src/components/*",
141
+ mode: "folder",
142
+ },
143
+ {
144
+ type: "helper",
145
+ pattern: "src/helpers/*.js",
146
+ mode: "file",
147
+ }
148
+ ]);
149
+ ```
150
+
151
+ > **💡 Tip:** Matchers with identical descriptors and options share the same cache instance for improved performance.
152
+
153
+ You can override the default options when creating a matcher:
154
+
155
+ ```typescript
156
+ const matcher = elements.getMatcher(
157
+ [/* descriptors */],
158
+ {
159
+ ignorePaths: ["**/*.test.ts"],
160
+ }
161
+ );
162
+ ```
163
+
164
+ ### Element Descriptors
165
+
166
+ Element descriptors define how files are identified and categorized. Each descriptor is an object with the following properties:
167
+
168
+ - **`pattern`** (`string | string[]`): Micromatch pattern(s) to match file paths
169
+ - **`type`** (`string`): The element type to assign to matching files
170
+ - **`category`** (`string`): Additional categorization for the element, providing another layer of classification
171
+ - **`mode`** (`"file" | "folder" | "full"`): Matching mode (default: `"folder"`)
172
+ - `"folder"`: Matches the first folder matching the pattern. The library will add `**/*` to the given pattern for matching files, because it needs to know exactly which folder has to be considered the element. So, you have to provide patterns matching the folder being the element, not the files directly.
173
+ - `"file"`: Matches files directly, but still matches progressively from the right. The provided pattern will not be modified.
174
+ - `"full"`: Matches the complete path.
175
+ - **`basePattern`** (`string`): Additional pattern that must match from the project root. Use it when using `file` or `folder` modes and you want to capture fragments from the rest of the path.
176
+ - **`capture`** (`string[]`): Array of keys to capture path fragments
177
+ - **`baseCapture`** (`string[]`): Array of keys to capture fragments from `basePattern`. If the same key is defined in both `capture` and `baseCapture`, the value from `capture` takes precedence.
178
+
179
+ ### Selectors
180
+
181
+ Selectors are used to match elements and dependencies against specific criteria. They are objects where each property represents a matching condition.
182
+
183
+ #### Element Properties
184
+
185
+ All element selectors support the following properties:
186
+
187
+ - **`type`** (`string | string[]`): Micromatch pattern(s) for the element type/s
188
+ - **`category`** (`string | string[]`): Micromatch pattern(s) for the element category/categories
189
+ - **`captured`** (`object`): Object with keys matching captured values. Each key can be a string or an array of strings representing micromatch patterns.
190
+ - **`origin`** (`"local" | "external" | "core"`): Element origin
191
+ - `local`: Files within the project
192
+ - `external`: External dependencies (e.g., `node_modules`)
193
+ - `core`: Core modules (e.g., Node.js built-ins)
194
+ - **`path`** (`string | string[]`): Micromatch pattern(s) for the file path
195
+ - **`elementPath`** (`string | string[]`): Pattern(s) for the element path
196
+ - **`internalPath`** (`string | string[]`): Pattern(s) for the path within the element. For file elements, it's the same as `elementPath`; for folder elements, it's relative to the folder.
197
+ - **`isIgnored`** (`boolean`): Whether the element is ignored
198
+ - **`isUnknown`** (`boolean`): Whether the element type is unknown (i.e., doesn't match any descriptor)
199
+
200
+ #### Dependency Properties
201
+
202
+ When matching dependencies, the `to` selector can additionally use:
203
+
204
+ - **`kind`** (`string | string[]`): Micromatch pattern(s) for the dependency kind
205
+ - **`relationship`** (`string | string[]`): Element relationship. Micromatch pattern(s) for the relationship between source and target elements:
206
+ - `internal`: Both files belong to the same element
207
+ - `child`: Target is a child of source
208
+ - `parent`: Target is a parent of source
209
+ - `sibling`: Elements share the same parent
210
+ - `uncle`: Target is a sibling of a source ancestor
211
+ - `nephew`: Target is a child of a source sibling
212
+ - `descendant`: Target is a descendant of source
213
+ - `ancestor`: Target is an ancestor of source
214
+ - **`specifiers`** (`string | string[]`): Pattern(s) for import/export specifiers (e.g., named imports)
215
+ - **`nodeKind`** (`string | string[]`): Pattern(s) for the AST node type causing the dependency (e.g., `"ImportDeclaration"`)
216
+ - **`source`** (`string | string[]`): Pattern(s) to match the source of the dependency. (e.g., the import path).
217
+ - **`baseSource`** (`string | string[]`): Pattern(s) for the base module name for external imports.
218
+
219
+ > **⚠️ Important:** All properties in a selector must match for the selector to be considered a match (AND logic). Use multiple selectors for OR logic.
220
+
221
+ > **Note:** You can also use the legacy selector syntax, but it’s deprecated and will be removed in a future release. See the [Legacy Selectors section](#legacy-selectors) for more details.
222
+
223
+ #### Template Variables
224
+
225
+ Selectors support template variables using [Handlebars syntax](https://handlebarsjs.com/) (`{{ variableName }}`). Templates are resolved at match time using:
226
+
227
+ - **Element properties** (`type`, `category`, `captured`, etc.)
228
+ - **Dependency properties** (`from`, `to`)
229
+
230
+ #### Available Template Data
231
+
232
+ When matching, the following data is automatically available:
233
+
234
+ **For element matching:**
235
+ - Properties of the element under match are available in the `element` object (type, category, captured, origin, path, etc.)
236
+
237
+ **For dependency matching:**
238
+ - `from`: Properties of the dependency source element
239
+ - `to`: Properties of the dependency target element, and properties of the dependency itself (source, kind, nodeKind, specifiers, etc.)
240
+
241
+ #### Template Examples
242
+
243
+ ```ts
244
+ // Using captured values in templates
245
+ const matcher = elements.getMatcher([
246
+ {
247
+ type: "component",
248
+ pattern: "src/modules/(*)/**/*.component.tsx",
249
+ capture: ["module", "elementName", "fileName"],
250
+ mode: "file"
251
+ }
252
+ ]);
253
+
254
+ // Match components from specific module using template
255
+ const isAuthComponent = matcher.isMatch(
256
+ "src/modules/auth/LoginForm.component.tsx",
257
+ {
258
+ type: "component",
259
+ captured: { module: "{{ element.captured.module }}" } // This will always match
260
+ },
261
+ );
262
+
263
+ // Using templates in dependency selectors
264
+ const isDependencyMatch = matcher.isMatch(
265
+ {
266
+ from: "src/components/Button.tsx",
267
+ to: "src/services/Api.ts",
268
+ source: "../services/Api",
269
+ kind: "type",
270
+ nodeKind: "ImportDeclaration",
271
+ specifiers: ["calculateSum", "calculateAvg"],
272
+ },
273
+ {
274
+ from: { type: "{{ from.type }}", captured: { fileName: "{{ from.captured.fileName }}" } },
275
+ to: { path: "{{ to.path }}", specifiers: "{{ lookup to.specifiers 0 }}", kind: "{{ to.kind }}" },
276
+ }
277
+ );
278
+ ```
279
+
280
+ #### Using Extra Template Data
281
+
282
+ You can provide additional template data using the `extraTemplateData` option in `MatcherOptions`:
283
+
284
+ ```ts
285
+ // Using templates in selectors
286
+ const isMatch = matcher.isMatch(
287
+ "src/components/UserProfile.tsx",
288
+ { type: "{{ componentType }}" },
289
+ {
290
+ extraTemplateData: { componentType: "component" }
291
+ }
292
+ );
293
+ ```
294
+
295
+ ### Using Matchers
296
+
297
+ You can use element selectors with a created matcher to check if a given path corresponds to an element with specific properties, or if a dependency between two paths matches certain criteria.
298
+
299
+ #### Element Matching
300
+
301
+ To match an element, use the `isMatch` method of the matcher, providing the file path and an element selector.
302
+
303
+ ```ts
304
+ const isElementMatch = matcher.isMatch("src/components/Button.tsx", { type: "component" });
305
+ ```
306
+
307
+ > [!TIP]
308
+ > You can also provide an array of selectors to the `isMatch` method. In this case, the method will return `true` if the element matches any of the provided selectors (OR logic).
309
+
310
+ #### Dependency Matching
311
+
312
+ To match a dependency, use the `isMatch` method of the matcher, providing the properties of the dependency and a dependency selector.
313
+
314
+ **Dependency object properties:**
315
+
316
+ - **`from`** (`string`): Source file path
317
+ - **`to`** (`string`): Target file path
318
+ - **`source`** (`string`): Import/export source as written in code
319
+ - **`kind`** (`string`): Import kind (`"type"`, `"value"`, etc.)
320
+ - **`nodeKind`** (`string`): AST node kind
321
+ - **`specifiers`** (`string[]`): Imported/exported names
322
+
323
+ **Dependency selector:**
324
+
325
+ - **`from`**: Element selector(s) for the source file
326
+ - **`to`**: Dependency selector(s) for the target file
327
+
328
+ ```ts
329
+ const isDependencyMatch = matcher.isMatch(
330
+ { // Dependency properties
331
+ from: "src/components/Button.tsx",
332
+ to: "src/services/Api.ts",
333
+ source: "../services/Api",
334
+ kind: "type",
335
+ nodeKind: "ImportDeclaration",
336
+ },
337
+ {
338
+ from: { category: "react" }, // Dependency source selector/s
339
+ to: { type: "service", nodeKind: "Import*" }, // Dependency target selector/s
340
+ }
341
+ );
342
+ ```
343
+
344
+ > [!TIP]
345
+ > You can also provide an array of selectors both to the `from` and `to` properties of the dependency selector. In this case, the method will return `true` if the dependency matches any combination of the provided selectors (OR logic).
346
+
347
+ ## API Reference
348
+
349
+ ### Class: Elements
350
+
351
+ #### Constructor
352
+
353
+ Creates a new `Elements` instance with optional default configuration.
354
+
355
+ ```ts
356
+ new Elements(options?: ConfigurationOptions);
357
+ ```
358
+
359
+ #### Methods
360
+
361
+ ##### `getMatcher`
362
+
363
+ Creates a new matcher instance.
364
+
365
+ - __Parameters__:
366
+ - `descriptors`: `array<ElementDescriptor>` Array of [element descriptors](#element-descriptors) to be used by the matcher.
367
+ - `options`: `ElementsOptions` Optional. [Configuration options](#configuration-options) for the matcher. These options will override the default options set in the `Elements` instance.
368
+ - __Returns__: `Matcher` A new matcher instance.
369
+
370
+ ```ts
371
+ const matcher = elements.getMatcher([
372
+ {
373
+ type: "component",
374
+ pattern: "src/components/*",
375
+ mode: "folder",
376
+ },
377
+ {
378
+ type: "helper",
379
+ pattern: "src/helpers/*.js",
380
+ mode: "file",
381
+ }
382
+ ]);
383
+ ```
384
+
385
+ ##### `clearCache`
386
+
387
+ Clears all cached matcher instances.
388
+
389
+ ```ts
390
+ elements.clearCache();
391
+ ```
392
+
393
+ ##### `serializeCache`
394
+
395
+ Serializes all cached matcher instances to a plain object.
396
+
397
+ ```ts
398
+ const cache = elements.serializeCache();
399
+ ```
400
+
401
+ ##### `setCacheFromSerialized`
402
+ Sets the cached matcher instances from a serialized object.
403
+
404
+ ```ts
405
+ const cache = elements.serializeCache();
406
+ elements.setCacheFromSerialized(cache);
407
+ ```
408
+
409
+ ### Matcher Instance Methods
410
+
411
+ #### `isMatch`
412
+
413
+ Checks if a given path matches the specified element or dependency selector.
414
+
415
+ ```ts
416
+ const isElementMatch = matcher.isMatch("src/components/Button.tsx", [{ type: "component" }]);
417
+
418
+ const isDependencyMatch = matcher.isMatch(
419
+ {
420
+ from: "src/components/Button.tsx",
421
+ to: "src/services/Api.ts",
422
+ source: "../services/Api",
423
+ kind: "type",
424
+ nodeKind: "ImportDeclaration",
425
+ },
426
+ {
427
+ from: [{ category: "react" }],
428
+ to: { type: "service", nodeKind: "Import*" },
429
+ }
430
+ );
431
+ ```
432
+
433
+ - __Parameters__:
434
+ - `path`:
435
+ - `string` The path to check when using an [element selector](#selectors).
436
+ - `DependencyProperties` The [properties of the dependency](#dependency-matching) to check when using a [dependency selector](#selectors).
437
+ - `selector`: `ElementSelector | DependencySelector` The [selector](#selectors) to match against. It can be either an element selector (for path matching) or a dependency selector (for dependency matching).
438
+ - If `path` is a string, `selector` should be an [`ElementSelector`](#selectors) or an array of `ElementSelector`.
439
+ - If `path` are dependency properties, `selector` should be a [`DependencySelector`](#selectors) or an array of `DependencySelector`.
440
+ - `options`: `MatcherOptions` Optional. Additional options for matching:
441
+ - `extraTemplateData`: `object` Optional. Extra data to pass to selector templates. When using [template variables](#template-variables) in selectors, this data will be available for rendering.
442
+
443
+ #### `getSelectorMatching`
444
+
445
+ Returns the first matching selector or `null`.
446
+
447
+ ```ts
448
+ const matchingSelector = matcher.getSelectorMatching("src/components/Button.tsx", [{ type: "component" }]);
449
+ ```
450
+
451
+ Parameters are the same as `isMatch`, but instead of returning a boolean, it returns the first matching selector or `null` if none match.
452
+
453
+ #### `describeElement`
454
+
455
+ Returns a detailed description of an element.
456
+
457
+ ```ts
458
+ const elementDescription = matcher.describeElement("src/components/Button.tsx");
459
+ ```
460
+
461
+ - __Parameters__:
462
+ - `path`: `string` The path of the element to describe.
463
+
464
+ #### `describeDependency`
465
+
466
+ Returns a detailed description of a dependency.
467
+
468
+ ```ts
469
+ const dependencyDescription = matcher.describeDependency({
470
+ from: "src/components/Button.tsx",
471
+ to: "src/services/Api.ts",
472
+ source: "../services/Api",
473
+ kind: "type",
474
+ nodeKind: "ImportDeclaration",
475
+ });
476
+ ```
477
+
478
+ - __Parameters__:
479
+ - `dependency`: The [properties of the dependency to describe](#dependency-matching).
480
+
481
+ #### `getSelectorMatchingDescription`
482
+
483
+ Matches a description against selectors. As first argument, it should receive the result of `describeElement` or `describeDependency`.
484
+
485
+ ```ts
486
+ const elementDescription = matcher.describeElement("src/components/Button.tsx");
487
+ const matchingSelector = matcher.getSelectorMatchingDescription(elementDescription, [{ type: "component" }]);
488
+ ```
489
+
490
+ #### `clearCache`
491
+
492
+ Clears the matcher's internal cache.
493
+
494
+ ```ts
495
+ matcher.clearCache();
496
+ ```
497
+
498
+ #### `serializeCache`
499
+
500
+ Serializes the matcher's cache.
501
+
502
+ ```ts
503
+ const cache = matcher.serializeCache();
504
+ ```
505
+
506
+ #### `setCacheFromSerialized`
507
+
508
+ Restores the matcher's cache from a serialized object.
509
+
510
+ ```ts
511
+ // Serialize cache to a serializable object
512
+ const cache = matcher.serializeCache();
513
+
514
+ // Clear current cache
515
+ matcher.clearCache();
516
+
517
+ // Restore cache from serialized object
518
+ matcher.setCacheFromSerialized(cache);
519
+ ```
520
+
521
+ ## Legacy Selectors
522
+
523
+ Legacy selectors are defined using a different syntax and are provided for backward compatibility. However, this syntax is deprecated and will be removed in a future release. It is recommended to migrate to the new selector syntax.
524
+
525
+ Selectors can be defined as either a string or an array of strings representing the element type(s):
526
+
527
+ ```ts
528
+ // Legacy selector using a string
529
+ const isElementMatch = matcher.isMatch("src/components/Button.tsx", "component");
530
+ // Legacy selector using an array of strings
531
+ const isElementMatch = matcher.isMatch("src/components/Button.tsx", ["component", "service"]);
532
+ ```
533
+
534
+ They can also be defined as an array where the first element is the type and the second element is an object containing captured values:
535
+
536
+ ```ts
537
+ // Legacy selector with captured values
538
+ const isElementMatch = matcher.isMatch(
539
+ "src/modules/auth/LoginForm.component.tsx",
540
+ ["component", { module: "auth" }]
541
+ );
542
+ ```
543
+
544
+ > **⚠️ Warning:** Avoid mixing legacy selectors with the new selector syntax in the same project, as this can lead to ambiguity. In particular, if you define a top-level array selector with two elements and the second one is an object containing a `type` or `category` key, it will be interpreted as legacy options rather than two separate selectors.
545
+
546
+ ## Contributing
547
+
548
+ Contributors are welcome.
549
+ Please read the [contributing guidelines](../../.github/CONTRIBUTING.md) and [code of conduct](../../.github/CODE_OF_CONDUCT.md).
550
+
551
+ ## License
552
+
553
+ MIT, see [LICENSE](./LICENSE) for details.
554
+
555
+ [coveralls-image]: https://coveralls.io/repos/github/javierbrea/eslint-plugin-boundaries/badge.svg
556
+ [coveralls-url]: https://coveralls.io/github/javierbrea/eslint-plugin-boundaries
557
+ [build-image]: https://github.com/javierbrea/eslint-plugin-boundaries/workflows/build/badge.svg
558
+ [build-url]: https://github.com/javierbrea/eslint-plugin-boundaries/actions?query=workflow%3Abuild+branch%3Amaster
559
+ [last-commit-image]: https://img.shields.io/github/last-commit/javierbrea/eslint-plugin-boundaries.svg
560
+ [last-commit-url]: https://github.com/javierbrea/eslint-plugin-boundaries/commits
561
+ [license-image]: https://img.shields.io/npm/l/eslint-plugin-boundaries.svg
562
+ [license-url]: https://github.com/javierbrea/eslint-plugin-boundaries/blob/master/LICENSE
563
+ [npm-downloads-image]: https://img.shields.io/npm/dm/@boundaries/elements.svg
564
+ [npm-downloads-url]: https://www.npmjs.com/package/@boundaries/elements
565
+ [quality-gate-image]: https://sonarcloud.io/api/project_badges/measure?project=javierbrea_eslint-plugin-boundaries_elements&metric=alert_status
566
+ [quality-gate-url]: https://sonarcloud.io/dashboard?id=javierbrea_eslint-plugin-boundaries_elements
567
+ [release-image]: https://img.shields.io/github/release-date/javierbrea/eslint-plugin-boundaries.svg
568
+ [release-url]: https://github.com/javierbrea/eslint-plugin-boundaries/releases