New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@compiled/eslint-plugin

Package Overview
Dependencies
Maintainers
4
Versions
48
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@compiled/eslint-plugin - npm Package Compare versions

Comparing version 0.11.0 to 0.12.0

57

dist/rules/no-css-prop-without-css-function/index.js

@@ -13,4 +13,27 @@ "use strict";

this.context = context;
this.jsxElement = (0, ast_1.traverseUpToJSXOpeningElement)(this.baseNode);
this.references = context.getScope().references;
this.ignoreIfImported = [];
this.excludeReactComponents = false;
this.parseOptions(this.context.options);
}
parseOptions(options) {
if (options.length === 0)
return;
if (options[0].ignoreIfImported && Array.isArray(options[0].ignoreIfImported)) {
this.ignoreIfImported = options[0].ignoreIfImported;
}
if (typeof options[0].excludeReactComponents === 'boolean') {
this.excludeReactComponents = options[0].excludeReactComponents;
}
else if (options[0].excludeReactComponents !== undefined) {
throw new Error(`Expected the excludeReactComponents option to be a boolean, actually got ${typeof options[0]
.excludeReactComponents}`);
}
}
importsIgnoredLibraries() {
if (!this.ignoreIfImported.length)
return;
return (0, ast_1.findTSLibraryImportDeclarations)(this.context, this.ignoreIfImported).length > 0;
}
handleIdentifier(node) {

@@ -28,8 +51,5 @@ var _a, _b, _c;

const isFunctionParameter = (_c = reference === null || reference === void 0 ? void 0 : reference.resolved) === null || _c === void 0 ? void 0 : _c.defs.find((def) => def.type === 'Parameter');
const jsxElement = (0, ast_1.traverseUpToJSXOpeningElement)(this.baseNode);
// css property on DOM elements are always fine, e.g.
// <div css={...}> instead of <MyComponent css={...}>
if (jsxElement &&
jsxElement.name.type === 'JSXIdentifier' &&
(0, ast_1.isDOMElement)(jsxElement.name.name)) {
if ((0, ast_1.isNodeDOMElement)(this.jsxElement)) {
return;

@@ -70,3 +90,3 @@ }

function* fix(fixer) {
const compiledImports = (0, ast_1.findTSCompiledImportDeclarations)(context);
const compiledImports = (0, ast_1.findTSLibraryImportDeclarations)(context);
const source = context.getSourceCode();

@@ -141,3 +161,8 @@ // The string that `css` from `@compiled/css` is imported as

run() {
this.findStyleNodes(this.baseNode.expression);
if (!this.importsIgnoredLibraries()) {
if (this.excludeReactComponents && !(0, ast_1.isNodeDOMElement)(this.jsxElement)) {
return;
}
this.findStyleNodes(this.baseNode.expression);
}
}

@@ -167,3 +192,21 @@ }

fixable: 'code',
schema: [],
schema: [
{
type: 'object',
properties: {
ignoreIfImported: {
type: 'array',
items: [
{
type: 'string',
},
],
},
excludeReactComponents: {
type: 'boolean',
},
},
additionalProperties: false,
},
],
},

@@ -170,0 +213,0 @@ create: createNoCssPropWithoutCssFunctionRule(),

12

dist/utils/ast.d.ts

@@ -17,5 +17,7 @@ import type { TSESTree, TSESLint } from '@typescript-eslint/utils';

/**
* Re-implementation of findCompiledImportDeclarations for typescript-eslint.
* Re-implementation of findLibraryImportDeclarations for typescript-eslint.
*
* Given a rule, return any `@compiled/react` import declarations in the source code.
* Given a rule, return all imports from the libraries defined in `source`
* in the file. If `source` is not specified, return all import statements
* from `@compiled/react`.
*

@@ -25,3 +27,3 @@ * @param context Rule context

*/
export declare const findTSCompiledImportDeclarations: (context: TSESLint.RuleContext<string, readonly unknown[]>) => TSESTree.ImportDeclaration[];
export declare const findTSLibraryImportDeclarations: (context: TSESLint.RuleContext<string, readonly unknown[]>, sources?: string[]) => TSESTree.ImportDeclaration[];
/**

@@ -31,6 +33,6 @@ * Returns whether the element is a DOM element, which is all lowercase...

*
* @param elementName
* @param jsxElement the JSX element to check
* @returns whether the element is a DOM element (true) or a React component (false)
*/
export declare const isDOMElement: (elementName: string) => boolean;
export declare const isNodeDOMElement: (jsxElement: TSESTree.JSXOpeningElement) => boolean;
/**

@@ -37,0 +39,0 @@ * Traverses up the AST until it reaches a JSXOpeningElement. Used in conjunction with

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.usesCompiledAPI = exports.findDeclarationWithImport = exports.traverseUpToJSXOpeningElement = exports.isDOMElement = exports.findTSCompiledImportDeclarations = exports.findLibraryImportDeclarations = void 0;
exports.usesCompiledAPI = exports.findDeclarationWithImport = exports.traverseUpToJSXOpeningElement = exports.isNodeDOMElement = exports.findTSLibraryImportDeclarations = exports.findLibraryImportDeclarations = void 0;
const constants_1 = require("./constants");
// WARNING
// context.getSourceCode() is deprecated, but we still use it here because
// the newer alternative, context.sourceCode, is not supported below
// ESLint 8.40.
//
// We can replace this with context.sourceCode once we are certain that
// all Compiled users are using ESLint 8.40+.
/**

@@ -25,5 +32,7 @@ * Given a rule, return all imports from the libraries defined in `source`

/**
* Re-implementation of findCompiledImportDeclarations for typescript-eslint.
* Re-implementation of findLibraryImportDeclarations for typescript-eslint.
*
* Given a rule, return any `@compiled/react` import declarations in the source code.
* Given a rule, return all imports from the libraries defined in `source`
* in the file. If `source` is not specified, return all import statements
* from `@compiled/react`.
*

@@ -33,8 +42,12 @@ * @param context Rule context

*/
const findTSCompiledImportDeclarations = (context) => {
const findTSLibraryImportDeclarations = (context, sources = [constants_1.COMPILED_IMPORT]) => {
return context
.getSourceCode()
.ast.body.filter((node) => node.type === 'ImportDeclaration' && node.source.value === constants_1.COMPILED_IMPORT);
.ast.body.filter((node) => node.type === 'ImportDeclaration' &&
typeof node.source.value === 'string' &&
sources.includes(node.source.value));
};
exports.findTSCompiledImportDeclarations = findTSCompiledImportDeclarations;
exports.findTSLibraryImportDeclarations = findTSLibraryImportDeclarations;
const isDOMElementName = (elementName) => elementName.charAt(0) !== elementName.charAt(0).toUpperCase() &&
elementName.charAt(0) === elementName.charAt(0).toLowerCase();
/**

@@ -44,8 +57,9 @@ * Returns whether the element is a DOM element, which is all lowercase...

*
* @param elementName
* @param jsxElement the JSX element to check
* @returns whether the element is a DOM element (true) or a React component (false)
*/
const isDOMElement = (elementName) => elementName.charAt(0) !== elementName.charAt(0).toUpperCase() &&
elementName.charAt(0) === elementName.charAt(0).toLowerCase();
exports.isDOMElement = isDOMElement;
const isNodeDOMElement = (jsxElement) => {
return jsxElement.name.type === 'JSXIdentifier' && isDOMElementName(jsxElement.name.name);
};
exports.isNodeDOMElement = isNodeDOMElement;
/**

@@ -52,0 +66,0 @@ * Traverses up the AST until it reaches a JSXOpeningElement. Used in conjunction with

{
"name": "@compiled/eslint-plugin",
"version": "0.11.0",
"version": "0.12.0",
"description": "A familiar and performant compile time CSS-in-JS library for React.",

@@ -5,0 +5,0 @@ "homepage": "https://compiledcssinjs.com/docs/pkg-eslint-plugin",

@@ -109,3 +109,3 @@ # `jsx-pragma`

### `runtime: 'classic' | 'automatic`
### `runtime: 'classic' | 'automatic'`

@@ -112,0 +112,0 @@ What [JSX runtime](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html) to adhere to,

@@ -133,2 +133,35 @@ import { outdent } from 'outdent';

`,
{
code: outdent`
/** @jsx jsx */
import { jsx } from '@emotion/react';
<div css={{ color: 'blue' }} />
`,
options: [{ ignoreIfImported: ['@emotion/react'] }],
},
{
code: outdent`
/** @jsx jsx */
import { jsx } from '@emotion/core';
<div css={{ color: 'blue' }} />
`,
options: [{ ignoreIfImported: ['styled-components', '@emotion/core'] }],
},
{
code: outdent`
import { css } from '@compiled/react';
import { jsx } from '@emotion/react';
<div css={{ color: 'blue' }} />
`,
options: [{ ignoreIfImported: ['@emotion/react'] }],
},
{
code: outdent`
<CustomComponent css={{ color: 'blue' }} />
`,
options: [{ excludeReactComponents: true }],
},
],

@@ -455,4 +488,22 @@

},
{
errors: [
{
messageId: 'noCssFunction',
},
],
code: outdent`
import { css } from '@compiled/react';
<div css={{ color: 'blue' }} />
`,
output: outdent`
import { css } from '@compiled/react';
<div css={css({ color: 'blue' })} />
`,
options: [{ ignoreIfImported: ['@emotion/react'] }],
},
],
}
);
import type { TSESTree, TSESLint } from '@typescript-eslint/utils';
import {
findTSCompiledImportDeclarations,
isDOMElement,
findTSLibraryImportDeclarations,
isNodeDOMElement,
traverseUpToJSXOpeningElement,

@@ -24,3 +24,3 @@ } from '../../utils/ast';

type Reference = TSESLint.Scope.Reference;
type Context = TSESLint.RuleContext<string, readonly []>;
type Context = TSESLint.RuleContext<string, readonly unknown[]>;

@@ -35,8 +35,39 @@ const findNodeReference = (

class NoCssPropWithoutCssFunctionRunner {
private excludeReactComponents: boolean;
private ignoreIfImported: string[];
private jsxElement: TSESTree.JSXOpeningElement;
private references: Reference[];
constructor(private baseNode: TSESTree.JSXExpressionContainer, private context: Context) {
this.jsxElement = traverseUpToJSXOpeningElement(this.baseNode);
this.references = context.getScope().references;
this.ignoreIfImported = [];
this.excludeReactComponents = false;
this.parseOptions(this.context.options as any);
}
private parseOptions(options: any[]) {
if (options.length === 0) return;
if (options[0].ignoreIfImported && Array.isArray(options[0].ignoreIfImported)) {
this.ignoreIfImported = options[0].ignoreIfImported;
}
if (typeof options[0].excludeReactComponents === 'boolean') {
this.excludeReactComponents = options[0].excludeReactComponents;
} else if (options[0].excludeReactComponents !== undefined) {
throw new Error(
`Expected the excludeReactComponents option to be a boolean, actually got ${typeof options[0]
.excludeReactComponents}`
);
}
}
private importsIgnoredLibraries() {
if (!this.ignoreIfImported.length) return;
return findTSLibraryImportDeclarations(this.context, this.ignoreIfImported).length > 0;
}
private handleIdentifier(node: TSESTree.Identifier) {

@@ -56,11 +87,5 @@ // Resolve the variable for the reference

const jsxElement = traverseUpToJSXOpeningElement(this.baseNode);
// css property on DOM elements are always fine, e.g.
// <div css={...}> instead of <MyComponent css={...}>
if (
jsxElement &&
jsxElement.name.type === 'JSXIdentifier' &&
isDOMElement(jsxElement.name.name)
) {
if (isNodeDOMElement(this.jsxElement)) {
return;

@@ -104,3 +129,3 @@ }

function* fix(fixer: TSESLint.RuleFixer) {
const compiledImports = findTSCompiledImportDeclarations(context);
const compiledImports = findTSLibraryImportDeclarations(context);
const source = context.getSourceCode();

@@ -177,3 +202,8 @@

run() {
this.findStyleNodes(this.baseNode.expression);
if (!this.importsIgnoredLibraries()) {
if (this.excludeReactComponents && !isNodeDOMElement(this.jsxElement)) {
return;
}
this.findStyleNodes(this.baseNode.expression);
}
}

@@ -212,5 +242,23 @@ }

fixable: 'code',
schema: [],
schema: [
{
type: 'object',
properties: {
ignoreIfImported: {
type: 'array',
items: [
{
type: 'string',
},
],
},
excludeReactComponents: {
type: 'boolean',
},
},
additionalProperties: false,
},
],
},
create: createNoCssPropWithoutCssFunctionRule(),
};

@@ -85,1 +85,26 @@ # `no-css-prop-without-css-function`

```
## Options
This rule supports the following options:
### `ignoreIfImported: string[]`
An array of libraries, each specified as a string (e.g. `['@emotion/core', '@emotion/react']`). If any of these libraries is detected as being imported in the current file, then this ESLint rule does not run.
This is useful if you do not want `no-css-prop-without-css-function` to conflict with files where Emotion's JSX pragma is being used (such as the below example).
```tsx
/** @jsx jsx */
import { jsx } from '@emotion/react';
// ...
```
This is an empty array by default.
### `excludeReactComponents: boolean`
Whether to exclude `css` attributes of React components from being affected by this ESLint rule. We assume that an element is a React component if it starts with a capital letter.
This is false by default.

@@ -7,2 +7,10 @@ import type { TSESTree, TSESLint } from '@typescript-eslint/utils';

// WARNING
// context.getSourceCode() is deprecated, but we still use it here because
// the newer alternative, context.sourceCode, is not supported below
// ESLint 8.40.
//
// We can replace this with context.sourceCode once we are certain that
// all Compiled users are using ESLint 8.40+.
/**

@@ -34,5 +42,7 @@ * Given a rule, return all imports from the libraries defined in `source`

/**
* Re-implementation of findCompiledImportDeclarations for typescript-eslint.
* Re-implementation of findLibraryImportDeclarations for typescript-eslint.
*
* Given a rule, return any `@compiled/react` import declarations in the source code.
* Given a rule, return all imports from the libraries defined in `source`
* in the file. If `source` is not specified, return all import statements
* from `@compiled/react`.
*

@@ -42,4 +52,5 @@ * @param context Rule context

*/
export const findTSCompiledImportDeclarations = (
context: TSESLint.RuleContext<string, readonly unknown[]>
export const findTSLibraryImportDeclarations = (
context: TSESLint.RuleContext<string, readonly unknown[]>,
sources = [COMPILED_IMPORT]
): TSESTree.ImportDeclaration[] => {

@@ -50,6 +61,12 @@ return context

(node): node is TSESTree.ImportDeclaration =>
node.type === 'ImportDeclaration' && node.source.value === COMPILED_IMPORT
node.type === 'ImportDeclaration' &&
typeof node.source.value === 'string' &&
sources.includes(node.source.value)
);
};
const isDOMElementName = (elementName: string): boolean =>
elementName.charAt(0) !== elementName.charAt(0).toUpperCase() &&
elementName.charAt(0) === elementName.charAt(0).toLowerCase();
/**

@@ -59,8 +76,8 @@ * Returns whether the element is a DOM element, which is all lowercase...

*
* @param elementName
* @param jsxElement the JSX element to check
* @returns whether the element is a DOM element (true) or a React component (false)
*/
export const isDOMElement = (elementName: string): boolean =>
elementName.charAt(0) !== elementName.charAt(0).toUpperCase() &&
elementName.charAt(0) === elementName.charAt(0).toLowerCase();
export const isNodeDOMElement = (jsxElement: TSESTree.JSXOpeningElement): boolean => {
return jsxElement.name.type === 'JSXIdentifier' && isDOMElementName(jsxElement.name.name);
};

@@ -67,0 +84,0 @@ /**

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc