@angular-eslint/eslint-plugin
Advanced tools
Comparing version
@@ -29,2 +29,3 @@ { | ||
"@angular-eslint/no-queries-metadata-property": "error", | ||
"@angular-eslint/no-uncalled-signals": "error", | ||
"@angular-eslint/pipe-prefix": "error", | ||
@@ -31,0 +32,0 @@ "@angular-eslint/prefer-inject": "error", |
@@ -31,2 +31,3 @@ declare const _default: { | ||
"@angular-eslint/no-queries-metadata-property": string; | ||
"@angular-eslint/no-uncalled-signals": string; | ||
"@angular-eslint/pipe-prefix": string; | ||
@@ -90,2 +91,3 @@ "@angular-eslint/prefer-inject": string; | ||
"no-lifecycle-call": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noLifecycleCall", [], import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>; | ||
"no-uncalled-signals": import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./rules/no-uncalled-signals").MessageIds, [], import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>; | ||
"no-output-native": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noOutputNative", [], import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>; | ||
@@ -92,0 +94,0 @@ "no-output-on-prefix": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noOutputOnPrefix", [], import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>; |
@@ -64,2 +64,3 @@ "use strict"; | ||
const no_queries_metadata_property_1 = __importStar(require("./rules/no-queries-metadata-property")); | ||
const no_uncalled_signals_1 = __importStar(require("./rules/no-uncalled-signals")); | ||
const pipe_prefix_1 = __importStar(require("./rules/pipe-prefix")); | ||
@@ -107,2 +108,3 @@ const prefer_on_push_component_change_detection_1 = __importStar(require("./rules/prefer-on-push-component-change-detection")); | ||
[no_lifecycle_call_1.RULE_NAME]: no_lifecycle_call_1.default, | ||
[no_uncalled_signals_1.RULE_NAME]: no_uncalled_signals_1.default, | ||
[no_output_native_1.RULE_NAME]: no_output_native_1.default, | ||
@@ -109,0 +111,0 @@ [no_output_on_prefix_1.RULE_NAME]: no_output_on_prefix_1.default, |
@@ -7,2 +7,3 @@ "use strict"; | ||
const create_eslint_rule_1 = require("../utils/create-eslint-rule"); | ||
const signals_1 = require("../utils/signals"); | ||
const DEFAULT_OPTIONS = { | ||
@@ -15,8 +16,2 @@ preferReadonlySignalProperties: true, | ||
}; | ||
const KNOWN_SIGNAL_TYPES = new Set([ | ||
'InputSignal', | ||
'ModelSignal', | ||
'Signal', | ||
'WritableSignal', | ||
]); | ||
const KNOWN_SIGNAL_CREATION_FUNCTIONS = new Set([ | ||
@@ -93,3 +88,3 @@ 'computed', | ||
type.typeName.type === utils_2.AST_NODE_TYPES.Identifier && | ||
KNOWN_SIGNAL_TYPES.has(type.typeName.name)) { | ||
signals_1.KNOWN_SIGNAL_TYPES.has(type.typeName.name)) { | ||
shouldBeReadonly = true; | ||
@@ -140,3 +135,3 @@ } | ||
shouldBeReadonly = | ||
name !== undefined && KNOWN_SIGNAL_TYPES.has(name); | ||
name !== undefined && signals_1.KNOWN_SIGNAL_TYPES.has(name); | ||
} | ||
@@ -143,0 +138,0 @@ } |
@@ -5,5 +5,6 @@ export type Options = [ | ||
readonly requireMeaning?: boolean; | ||
readonly requireCustomId?: boolean | string; | ||
} | ||
]; | ||
export type MessageIds = 'requireLocalizeDescription' | 'requireLocalizeMeaning'; | ||
export type MessageIds = 'requireLocalizeDescription' | 'requireLocalizeMeaning' | 'requireLocalizeCustomId'; | ||
export declare const RULE_NAME = "require-localize-metadata"; | ||
@@ -10,0 +11,0 @@ declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageIds, Options, import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>; |
@@ -9,8 +9,7 @@ "use strict"; | ||
requireMeaning: false, | ||
requireCustomId: false, | ||
}; | ||
const VALID_LOCALIZED_STRING_WITH_DESCRIPTION = new RegExp(/:(.*\|)?([\w\s]+){1}(@@.*)?:.+/); | ||
const VALID_LOCALIZED_STRING_WITH_MEANING = new RegExp(/:([\w\s]+\|)(.*)?(@@.*)?:.+/); | ||
const STYLE_GUIDE_LINK = 'https://angular.dev/guide/i18n'; | ||
const STYLE_GUIDE_LINK_COMMON_PREPARE = `${STYLE_GUIDE_LINK}-common-prepare`; | ||
const STYLE_GUIDE_LINK_METADATA_FOR_TRANSLATION = `${STYLE_GUIDE_LINK_COMMON_PREPARE}#i18n-metadata-for-translation`; | ||
const STYLE_GUIDE_LINK_PREPARE = `${STYLE_GUIDE_LINK}/prepare`; | ||
const STYLE_GUIDE_LINK_METADATA_FOR_TRANSLATION = `${STYLE_GUIDE_LINK_PREPARE}#i18n-metadata-for-translation`; | ||
exports.RULE_NAME = 'require-localize-metadata'; | ||
@@ -36,2 +35,6 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({ | ||
}, | ||
requireCustomId: { | ||
oneOf: [{ type: 'boolean' }, { type: 'string' }], | ||
default: DEFAULT_OPTIONS.requireCustomId, | ||
}, | ||
}, | ||
@@ -44,9 +47,10 @@ additionalProperties: false, | ||
requireLocalizeMeaning: `$localize tagged messages should contain a meaning. See more at ${STYLE_GUIDE_LINK_METADATA_FOR_TRANSLATION}`, | ||
requireLocalizeCustomId: `$localize tagged messages should contain a custom id{{patternMessage}}. See more at ${STYLE_GUIDE_LINK_METADATA_FOR_TRANSLATION}`, | ||
}, | ||
}, | ||
defaultOptions: [DEFAULT_OPTIONS], | ||
create(context, [{ requireDescription, requireMeaning }]) { | ||
create(context, [{ requireDescription, requireMeaning, requireCustomId }]) { | ||
return { | ||
TaggedTemplateExpression(taggedTemplateExpression) { | ||
if ((requireDescription || requireMeaning) && | ||
if ((requireDescription || requireMeaning || requireCustomId) && | ||
utils_1.ASTUtils.isIdentifier(taggedTemplateExpression.tag)) { | ||
@@ -56,5 +60,4 @@ const identifierName = taggedTemplateExpression.tag.name; | ||
if (identifierName === '$localize' && !!templateElement) { | ||
const templateElementRawValue = templateElement.value.raw; | ||
if (requireDescription && | ||
!VALID_LOCALIZED_STRING_WITH_DESCRIPTION.test(templateElementRawValue)) { | ||
const metadata = parseMetadata(templateElement.value.raw.trim()); | ||
if (requireDescription && !metadata.description) { | ||
context.report({ | ||
@@ -65,4 +68,3 @@ loc: templateElement.loc, | ||
} | ||
if (requireMeaning && | ||
!VALID_LOCALIZED_STRING_WITH_MEANING.test(templateElementRawValue)) { | ||
if (requireMeaning && !metadata.meaning) { | ||
context.report({ | ||
@@ -73,2 +75,17 @@ loc: templateElement.loc, | ||
} | ||
if (requireCustomId && | ||
!(metadata.customId && | ||
(typeof requireCustomId === 'string' | ||
? RegExp(requireCustomId).test(metadata.customId) | ||
: true))) { | ||
context.report({ | ||
loc: templateElement.loc, | ||
messageId: 'requireLocalizeCustomId', | ||
data: { | ||
patternMessage: typeof requireCustomId === 'string' | ||
? ` matching the pattern /${requireCustomId}/ on '${metadata.customId}'` | ||
: '', | ||
}, | ||
}); | ||
} | ||
} | ||
@@ -80,1 +97,31 @@ } | ||
}); | ||
// see https://github.com/angular/angular/blob/main/packages/localize/src/utils/src/messages.ts#L247 | ||
const BLOCK_MARKER = ':'; | ||
const MEANING_SEPARATOR = '|'; | ||
const ID_SEPARATOR = '@@'; | ||
function parseMetadata(rawText) { | ||
const output = { | ||
rawText, | ||
meaning: undefined, | ||
description: undefined, | ||
customId: undefined, | ||
}; | ||
if (rawText.charAt(0) !== BLOCK_MARKER) { | ||
return output; | ||
} | ||
const endOfTheBlock = rawText.lastIndexOf(BLOCK_MARKER); | ||
if (endOfTheBlock === -1) { | ||
return output; | ||
} | ||
const text = rawText.slice(1, endOfTheBlock); | ||
const [meaningAndDesc, customId] = text.split(ID_SEPARATOR, 2); | ||
let [meaning, description] = meaningAndDesc.split(MEANING_SEPARATOR, 2); | ||
if (description === undefined) { | ||
description = meaning; | ||
meaning = undefined; | ||
} | ||
if (description === '') { | ||
description = undefined; | ||
} | ||
return { rawText, meaning, description, customId }; | ||
} |
@@ -7,2 +7,3 @@ "use strict"; | ||
const DEFAULT_ORDER = { | ||
// https://angular.dev/api/core/Component | ||
Component: [ | ||
@@ -17,8 +18,46 @@ 'selector', | ||
'styles', | ||
'providers', | ||
'changeDetection', | ||
'encapsulation', | ||
'changeDetection', | ||
'viewProviders', | ||
'host', | ||
'hostDirectives', | ||
'inputs', | ||
'outputs', | ||
'animations', | ||
'schemas', | ||
'exportAs', | ||
'queries', | ||
'preserveWhitespaces', | ||
'jit', | ||
// Deprecated properties according to https://angular.dev/api/core/Component | ||
'moduleId', | ||
'interpolation', | ||
], | ||
Directive: ['selector', 'standalone'], | ||
NgModule: ['declarations', 'imports', 'exports', 'providers', 'bootstrap'], | ||
Pipe: ['name', 'standalone'], | ||
// https://angular.dev/api/core/Directive | ||
Directive: [ | ||
'selector', | ||
'standalone', | ||
'providers', | ||
'host', | ||
'hostDirectives', | ||
'inputs', | ||
'outputs', | ||
'exportAs', | ||
'queries', | ||
'jit', | ||
], | ||
// https://angular.dev/api/core/NgModule | ||
NgModule: [ | ||
'id', // rarely used but good to have first if set | ||
'imports', | ||
'declarations', | ||
'providers', | ||
'exports', | ||
'bootstrap', | ||
'schemas', | ||
'jit', | ||
], | ||
// https://angular.dev/api/core/Pipe | ||
Pipe: ['name', 'standalone', 'pure'], | ||
}; | ||
@@ -25,0 +64,0 @@ exports.RULE_NAME = 'sort-keys-in-type-decorator'; |
{ | ||
"name": "@angular-eslint/eslint-plugin", | ||
"version": "20.0.0-alpha.1", | ||
"version": "20.0.0-alpha.2", | ||
"description": "ESLint plugin for Angular applications, following https://angular.dev/style-guide", | ||
@@ -21,7 +21,7 @@ "license": "MIT", | ||
"dependencies": { | ||
"@angular-eslint/bundled-angular-compiler": "20.0.0-alpha.1", | ||
"@angular-eslint/utils": "20.0.0-alpha.1" | ||
"@angular-eslint/bundled-angular-compiler": "20.0.0-alpha.2", | ||
"@angular-eslint/utils": "20.0.0-alpha.2" | ||
}, | ||
"devDependencies": { | ||
"@angular-eslint/test-utils": "20.0.0-alpha.1" | ||
"@angular-eslint/test-utils": "20.0.0-alpha.2" | ||
}, | ||
@@ -28,0 +28,0 @@ "peerDependencies": { |
@@ -67,2 +67,3 @@ # @angular-eslint/eslint-plugin | ||
| [`no-queries-metadata-property`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/no-queries-metadata-property.md) | Disallows usage of the `queries` metadata property. See more at https://angular.dev/style-guide#style-05-12. | | | | | ||
| [`no-uncalled-signals`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/no-uncalled-signals.md) | Warns user about unintentionally doing logic on the signal, rather than the signal's value | | | :bulb: | | ||
| [`pipe-prefix`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/pipe-prefix.md) | Enforce consistent prefix for pipes. | | | | | ||
@@ -69,0 +70,0 @@ | [`prefer-inject`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-inject.md) | Prefer using the inject() function over constructor parameter injection | :white_check_mark: | | | |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
228203
3.1%153
4.08%3964
3.93%95
1.06%+ Added
+ Added
- Removed
- Removed
Updated