Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- [#84](https://github.com/green-code-initiative/creedengo-javascript/pull/84) Add rule GCI535 "No imported number format library"

### Changed

- [#48](https://github.com/green-code-initiative/creedengo-javascript/issues/48) Extend rule GCI530 "no-torch" to detect HTML5 Web API usage (`MediaTrackConstraints` torch constraint via `applyConstraints`)

## [3.1.0] - 2026-05-10

### Added
Expand Down
13 changes: 12 additions & 1 deletion eslint-plugin/docs/rules/no-torch.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,27 @@ As a developer, you should avoid programmatically enabling torch mode.

The flashlight can significantly drain the device's battery. If it is turned on without the user's knowledge, it could lead to unwanted battery consumption.

### React Native

```js
import Torch from "react-native-torch"; // Not-compliant

import axios from "axios"; // Compliant
```

### HTML5 Web API (MediaTrackConstraints)

```js
import axios from "axios"; // Compliant
// Not-compliant
await track.applyConstraints({ advanced: [{ torch: true }] });

// Compliant
await track.applyConstraints({ advanced: [{ facingMode: "environment" }] });
```

## Resources

### Documentation

- [CNUMR best practices mobile](https://github.com/cnumr/best-practices-mobile) - Torch free
- [MediaTrackConstraints: torch property (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#torch)
57 changes: 53 additions & 4 deletions eslint-plugin/lib/rules/no-torch.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,46 @@

"use strict";

const reactNativeTorchLibrary = "react-native-torch";

function getPropertyName(prop) {
if (prop.key.type === "Identifier") return prop.key.name;
if (prop.key.type === "Literal") return String(prop.key.value);
return null;
}

function findProperty(objectExpression, name) {
return objectExpression.properties.find(
(p) => p.type === "Property" && getPropertyName(p) === name
);
}

function objectHasTorchProperty(objectExpression) {
return Boolean(findProperty(objectExpression, "torch"));
}

function advancedArrayHasTorch(arrayExpression) {
return arrayExpression.elements.some(
(el) => el && el.type === "ObjectExpression" && objectHasTorchProperty(el)
);
}

function constraintsArgUsesTorchInAdvanced(arg) {
if (arg.type !== "ObjectExpression") return false;
const advancedProp = findProperty(arg, "advanced");
if (!advancedProp || advancedProp.value.type !== "ArrayExpression") return false;
return advancedArrayHasTorch(advancedProp.value);
}

function isApplyConstraintsCall(node) {
const { callee } = node;
if (callee.type !== "MemberExpression") return false;
const methodName = callee.computed
? callee.property.type === "Literal" && callee.property.value
: callee.property.name;
return methodName === "applyConstraints" && node.arguments.length > 0;
}

/** @type {import("eslint").Rule.RuleModule} */
module.exports = {
meta: {
Expand All @@ -34,12 +74,21 @@ module.exports = {
schema: [],
},
create: function (context) {
const reactNativeTorchLibrary = "react-native-torch";

return {
ImportDeclaration(node) {
const currentLibrary = node.source.value;
if (currentLibrary === reactNativeTorchLibrary) {
if (node.source.value === reactNativeTorchLibrary) {
context.report({
node,
messageId: "ShouldNotProgrammaticallyEnablingTorchMode",
});
}
},

CallExpression(node) {
if (
isApplyConstraintsCall(node) &&
constraintsArgUsesTorchInAdvanced(node.arguments[0])
) {
context.report({
node,
messageId: "ShouldNotProgrammaticallyEnablingTorchMode",
Expand Down
19 changes: 16 additions & 3 deletions eslint-plugin/tests/lib/rules/no-torch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,29 @@ const expectedError = {

const tests = {
valid: [
`
import axios from 'axios';
`,
`import axios from 'axios';`,
`track.applyConstraints({ advanced: [{ facingMode: 'environment' }] });`,
`track.applyConstraints({ advanced: [] });`,
`track.applyConstraints({ width: 1280, height: 720 });`,
],

invalid: [
{
code: "import Torch from 'react-native-torch';",
errors: [expectedError],
},
{
code: "track.applyConstraints({ advanced: [{ torch: true }] });",
errors: [expectedError],
},
{
code: "track.applyConstraints({ advanced: [{ torch: false }] });",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test case should be a valid one no ? Activating torch is the problematic one, or the aim is to avoid using torch feature at all ?
@utarwyn any thought on this ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After a little thought, to me it should be a compliant test case, waiting for more input

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes this should be valid 👍

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll work on it when I'll have a minute

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HuMoreau if you have some time to work on it 🤞
thank you !

errors: [expectedError],
},
{
code: "track.applyConstraints({ advanced: [{ facingMode: 'environment' }, { torch: true }] });",
errors: [expectedError],
},
],
};

Expand Down
11 changes: 11 additions & 0 deletions test-project/src/no-torch.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
// React Native
import Torch from "react-native-torch"; // Non-compliant: torch should not be enabled

Torch.switchState(true);

// Web API (MediaTrackConstraints)
export async function example() {
const mediaStream = await navigator.mediaDevices.getUserMedia({ video: true });
const [track] = mediaStream.getVideoTracks();

await track.applyConstraints({ advanced: [{ torch: true }] }); // Non-compliant: programmatically enables torch via advanced constraints

await track.applyConstraints({ advanced: [{ facingMode: "environment" }] }); // Compliant: no torch constraint
}