@dxos/eslint-plugin-rules 0.5.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/.eslintrc.js ADDED
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ "extends": [
3
+ "../../.eslintrc.js"
4
+ ],
5
+ "parserOptions": {
6
+ "project": "tsconfig.json",
7
+ "tsconfigRootDir": __dirname,
8
+ }
9
+ }
package/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ MIT License
2
+ Copyright (c) 2022 DXOS
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5
+
6
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # @dxos/eslint-plugin-rules
2
+
3
+ ## Installation
4
+
5
+ ```bash
6
+ pnpm i @dxos/eslint-plugin-rules
7
+ ```
8
+
9
+ ## DXOS Resources
10
+
11
+ - [Website](https://dxos.org)
12
+ - [Developer Documentation](https://docs.dxos.org)
13
+ - Talk to us on [Discord](https://discord.gg/eXVfryv3sW)
14
+
15
+ ## Contributions
16
+
17
+ Your ideas, issues, and code are most welcome. Please take a look at our [community code of conduct](https://github.com/dxos/dxos/blob/main/CODE_OF_CONDUCT.md), the [issue guide](https://github.com/dxos/dxos/blob/main/CONTRIBUTING.md#submitting-issues), and the [PR contribution guide](https://github.com/dxos/dxos/blob/main/CONTRIBUTING.md#submitting-prs).
18
+
19
+ License: [MIT](./LICENSE) Copyright 2022 © DXOS
package/docs/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # @dxos/eslint-plugin-rules
2
+
3
+
4
+
5
+ ## Dependency Graph
6
+
7
+ ```mermaid
8
+ %%{ init: {'flowchart':{'curve':'basis'}} }%%
9
+
10
+ flowchart LR
11
+
12
+ %% Classes
13
+ classDef def fill:#fff,stroke:#333,stroke-width:1px
14
+ classDef root fill:#fff,stroke:#333,stroke-width:4px
15
+
16
+ %% Nodes
17
+
18
+ subgraph undefined [undefined]
19
+ style undefined fill:#faf7eb,stroke:#333
20
+ dxos/eslint-plugin-rules("@dxos/eslint-plugin-rules"):::root
21
+ click dxos/eslint-plugin-rules "dxos/dxos/tree/main/tools/eslint-rules/docs"
22
+ end
23
+
24
+ %% Links
25
+ linkStyle default stroke:#333,stroke-width:1px
26
+ ```
27
+
28
+ ## Dependencies
29
+
30
+ | Module | Direct |
31
+ |---|---|
package/index.js ADDED
@@ -0,0 +1,10 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ module.exports = {
6
+ rules: {
7
+ comment: require('./rules/comment'),
8
+ header: require('./rules/header'),
9
+ },
10
+ };
package/package.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@dxos/eslint-plugin-rules",
3
+ "version": "0.5.0",
4
+ "private": false,
5
+ "homepage": "https://dxos.org",
6
+ "bugs": "https://github.com/dxos/dxos/issues",
7
+ "license": "MIT",
8
+ "author": "info@dxos.org",
9
+ "main": "index.js"
10
+ }
package/project.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
3
+ "name": "eslint-rules",
4
+ "sourceRoot": "tools/eslint-rules",
5
+ "projectType": "library",
6
+ "targets": {
7
+ "lint": {}
8
+ }
9
+ }
@@ -0,0 +1,226 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ // Based on https://github.com/eslint/eslint/blob/305e14af8bd12afc01487abee5c9b0f3eaca989e/lib/rules/capitalized-comments.js
6
+
7
+ 'use strict';
8
+
9
+ // ------------------------------------------------------------------------------
10
+ // Requires
11
+ // ------------------------------------------------------------------------------
12
+
13
+ const HEADER_PATTERN = require('./header').pattern;
14
+
15
+ // ------------------------------------------------------------------------------
16
+ // Helpers
17
+ // ------------------------------------------------------------------------------
18
+
19
+ const LETTER_PATTERN =
20
+ /[A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B2\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB5F\uAB64\uAB65\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF40\uDF42-\uDF49\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDE00-\uDE11\uDE13-\uDE2B\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDE00-\uDE2F\uDE44\uDE80-\uDEAA]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF98]|[\uD80C\uD840-\uD868\uD86A-\uD86C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D]|\uD87E[\uDC00-\uDE1D]/u;
21
+ const NUMBER_PATTERN = /[0-9]/;
22
+ const DEFAULT_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/u;
23
+ const WHITESPACE = /\s/gu;
24
+ const MAYBE_URL = /^\s*[^:/?#\s]+:\/\/[^?#]/u;
25
+ // Based on https://github.com/shinnn/github-username-regex/blob/0794566cc10e8c5a0e562823f8f8e99fa044e5f4/module.js.
26
+ const GITHUB_USERNAME = /[a-zA-Z\d](?:[a-zA-Z\d]|-(?=[a-zA-Z\d])){0,38}/;
27
+ const TODO_PATTERN = /todo/i;
28
+ const TODO_START_PATTERN = /^\s?todo/i;
29
+ const TODO_REMOVE_PATTERN = new RegExp(/TODO\(/.source + GITHUB_USERNAME.source + /\):/.source, '');
30
+ const FORMATTED_TODO_PATTERN = new RegExp(/^\s/.source + TODO_REMOVE_PATTERN.source + /\s/.source, '');
31
+
32
+ /*
33
+ * Base schema body for defining the basic capitalization rule, ignorePattern,
34
+ * and ignoreInlineComments values.
35
+ * This can be used in a few different ways in the actual schema.
36
+ */
37
+ const DEFAULTS = {
38
+ ignorePattern: 'ignore|code',
39
+ };
40
+
41
+ /**
42
+ * Get normalized options for either block or line comments from the given
43
+ * user-provided options.
44
+ * - If the user-provided options is just a string, returns a normalized
45
+ * set of options using default values for all other options.
46
+ * - If the user-provided options is an object, then a normalized option
47
+ * set is returned. Options specified in overrides will take priority
48
+ * over options specified in the main options object, which will in
49
+ * turn take priority over the rule's defaults.
50
+ * @param {Object|string} rawOptions The user-provided options.
51
+ * @param {string} which Either 'line' or 'block'.
52
+ * @returns {Object} The normalized options.
53
+ */
54
+ const getNormalizedOptions = (rawOptions, which) => Object.assign({}, DEFAULTS, rawOptions[which] || rawOptions);
55
+
56
+ /**
57
+ * Get normalized options for block and line comments.
58
+ * @param {Object|string} rawOptions The user-provided options.
59
+ * @returns {Object} An object with 'Line' and 'Block' keys and corresponding
60
+ * normalized options objects.
61
+ */
62
+ const getAllNormalizedOptions = (rawOptions = {}) => ({
63
+ Line: getNormalizedOptions(rawOptions, 'line'),
64
+ Block: getNormalizedOptions(rawOptions, 'block'),
65
+ });
66
+
67
+ /**
68
+ * Creates a regular expression for each ignorePattern defined in the rule
69
+ * options.
70
+ *
71
+ * This is done in order to avoid invoking the RegExp constructor repeatedly.
72
+ * @param {Object} normalizedOptions The normalized rule options.
73
+ * @returns {void}
74
+ */
75
+ const createRegExpForIgnorePatterns = (normalizedOptions) => {
76
+ Object.keys(normalizedOptions).forEach((key) => {
77
+ const ignorePatternStr = normalizedOptions[key].ignorePattern;
78
+
79
+ if (ignorePatternStr) {
80
+ const regExp = RegExp(`^\\s*(?:${ignorePatternStr})`, 'u');
81
+
82
+ normalizedOptions[key].ignorePatternRegExp = regExp;
83
+ }
84
+ });
85
+ };
86
+
87
+ // ------------------------------------------------------------------------------
88
+ // Rule Definition
89
+ // ------------------------------------------------------------------------------
90
+
91
+ module.exports = {
92
+ meta: {
93
+ type: 'layout',
94
+
95
+ docs: {
96
+ description: 'enforce proper comment format',
97
+ url: 'https://github.com/dxos/eng/blob/main/docs/content/general/writing-code.md#comments',
98
+ },
99
+
100
+ schema: [],
101
+ },
102
+
103
+ create: (context) => {
104
+ const normalizedOptions = getAllNormalizedOptions(context.options[1]);
105
+ const sourceCode = context.getSourceCode();
106
+
107
+ createRegExpForIgnorePatterns(normalizedOptions);
108
+
109
+ // ----------------------------------------------------------------------
110
+ // Helpers
111
+ // ----------------------------------------------------------------------
112
+
113
+ /**
114
+ * Check a comment to determine if it is valid for this rule.
115
+ * @param {ASTNode} comment The comment node to process.
116
+ * @param {Object} options The options for checking this comment.
117
+ * @returns {boolean} True if the comment is valid, false otherwise.
118
+ */
119
+ const isCommentValid = (comment, options) => {
120
+ // 0. Ignore JSDoc.
121
+ // TODO(wittjosiah): Parse JSDoc.
122
+ if (comment.value[0] === '*') {
123
+ return true;
124
+ }
125
+
126
+ // 1. Check for header pattern.
127
+ if (HEADER_PATTERN.test(comment.value)) {
128
+ return true;
129
+ }
130
+
131
+ // 2. Check for default ignore pattern.
132
+ if (DEFAULT_IGNORE_PATTERN.test(comment.value)) {
133
+ return true;
134
+ }
135
+
136
+ // 3. Check for custom ignore pattern.
137
+ const commentWithoutAsterisks = comment.value.replace(/\*/gu, '');
138
+
139
+ if (options.ignorePatternRegExp && options.ignorePatternRegExp.test(commentWithoutAsterisks)) {
140
+ return true;
141
+ }
142
+
143
+ // 4. Does the comment start with a possible URL?
144
+ if (MAYBE_URL.test(commentWithoutAsterisks)) {
145
+ return true;
146
+ }
147
+
148
+ // 5. Is the comment empty?
149
+ const commentWordCharsOnly = commentWithoutAsterisks.replace(WHITESPACE, '');
150
+
151
+ if (commentWordCharsOnly.length === 0) {
152
+ return true;
153
+ }
154
+
155
+ // 6. Check TODO format.
156
+ const containsTodo = TODO_PATTERN.test(comment.value);
157
+ const startsWithTodo = TODO_START_PATTERN.test(comment.value);
158
+ const isTodoFormatted = FORMATTED_TODO_PATTERN.test(comment.value);
159
+
160
+ if (containsTodo && !(startsWithTodo && isTodoFormatted)) {
161
+ return false;
162
+ }
163
+
164
+ const commentWithoutTodo = commentWordCharsOnly.replace(TODO_REMOVE_PATTERN, '');
165
+
166
+ // 6. Check the final character is punctuation.
167
+ const sentences = commentWithoutTodo
168
+ .replace(/`(.*?)`/g, '``') // Ignore everything inside backticks.
169
+ .split(/[.|?|!]\)?/);
170
+
171
+ if (sentences[sentences.length - 1] !== '') {
172
+ return false;
173
+ }
174
+
175
+ // 7. Check the case of the initial word character for each sentence.
176
+ for (const sentence in sentences) {
177
+ const firstChar = sentences[sentence][0];
178
+
179
+ if (typeof firstChar !== 'string') {
180
+ continue;
181
+ }
182
+
183
+ const isLetter = LETTER_PATTERN.test(firstChar);
184
+ const isNumber = NUMBER_PATTERN.test(firstChar);
185
+ const isAllowableChar = ['`', '"', '(', '-'].includes(firstChar);
186
+ const isLowercase = firstChar !== firstChar.toLocaleUpperCase();
187
+
188
+ if (!(isLetter || isNumber || isAllowableChar) || isLowercase) {
189
+ return false;
190
+ }
191
+ }
192
+
193
+ return true;
194
+ };
195
+
196
+ /**
197
+ * Process a comment to determine if it needs to be reported.
198
+ * @param {ASTNode} comment The comment node to process.
199
+ * @returns {void}
200
+ */
201
+ const processComment = (comment) => {
202
+ const options = normalizedOptions[comment.type];
203
+ const commentValid = isCommentValid(comment, options);
204
+
205
+ if (!commentValid) {
206
+ context.report({
207
+ node: null, // Intentionally using loc instead
208
+ loc: comment.loc,
209
+ message: 'Comments must be formatted correctly',
210
+ });
211
+ }
212
+ };
213
+
214
+ // ----------------------------------------------------------------------
215
+ // Public
216
+ // ----------------------------------------------------------------------
217
+
218
+ return {
219
+ Program: (node) => {
220
+ const comments = sourceCode.getAllComments();
221
+
222
+ comments.filter((token) => token.type !== 'Shebang').forEach(processComment);
223
+ },
224
+ };
225
+ },
226
+ };
@@ -0,0 +1,32 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ const REGEX = /Copyright [0-9]+ DXOS.org/;
6
+ const TEMPLATE = ['//', `// Copyright ${new Date().getFullYear()} DXOS.org`, '//', ''].join('\n') + '\n';
7
+
8
+ module.exports = {
9
+ pattern: REGEX,
10
+ meta: {
11
+ type: 'layout',
12
+
13
+ docs: {
14
+ description: 'enforce copyright header',
15
+ },
16
+ fixable: 'code',
17
+ schema: [],
18
+ },
19
+ create: (context) => {
20
+ return {
21
+ Program: (node) => {
22
+ if (!context.getSource().match(REGEX)) {
23
+ context.report({
24
+ node,
25
+ message: 'Missing copyright header',
26
+ fix: (fixer) => fixer.insertTextBefore(node, TEMPLATE),
27
+ });
28
+ }
29
+ },
30
+ };
31
+ },
32
+ };