@friendsoftheweb/eslint-plugin 0.0.1 → 0.0.2

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/README.md CHANGED
@@ -6,9 +6,59 @@
6
6
  yarn add -D @friendsoftheweb/eslint-plugin
7
7
  ```
8
8
 
9
+ ## Rules
10
+
11
+ ### `friendsoftheweb/ban-lodash-import`
12
+
13
+ Enforces importing functions from `lodash-es` instead of `lodash`.
14
+
15
+ This rule helps ensure tree shaking works properly by preferring ES module
16
+ imports from `lodash-es` over CommonJS imports from `lodash`.
17
+
18
+ #### Examples
19
+
20
+ ❌ **Incorrect:**
21
+
22
+ ```javascript
23
+ import _ from 'lodash';
24
+ import { map } from 'lodash';
25
+ import debounce from 'lodash/debounce';
26
+ ```
27
+
28
+ ✅ **Correct:**
29
+
30
+ ```javascript
31
+ import { map } from 'lodash-es';
32
+ import debounce from 'lodash-es/debounce';
33
+ ```
34
+
35
+ **Note:** This rule provides automatic fixes for most cases, except for
36
+ `lodash/fp` imports which require manual migration.
37
+
38
+ ### `friendsoftheweb/css-module-class-exists`
39
+
40
+ Enforces that class names used from an imported CSS module exist in the module
41
+ file.
42
+
43
+ ### `friendsoftheweb/css-module-name-matches`
44
+
45
+ Enforces that a CSS module's filename matches the filename of the importing
46
+ file.
47
+
48
+ This rule ensures consistent naming conventions by requiring CSS module files to
49
+ have the same base name as the file importing them.
50
+
51
+ ### `friendsoftheweb/valid-server-actions-path`
52
+
53
+ Enforces server actions are exported from file paths that match
54
+ `app/**/_actions.ts` or `app/**/_actions/**/*.ts`.
55
+
56
+ This rule helps maintain a consistent file structure for Next.js server actions
57
+ by ensuring they are placed in designated locations.
58
+
9
59
  ## Example Configurations
10
60
 
11
- ## Basic Example
61
+ ### Basic Example
12
62
 
13
63
  ```javascript
14
64
  import friendsOfTheWeb from '@friendsoftheweb/eslint-plugin';
@@ -27,7 +77,29 @@ export default defineConfig([
27
77
  ]);
28
78
  ```
29
79
 
30
- ## Example with React
80
+ ### Gradual Adoption
81
+
82
+ There is an additional configuration that makes it easier to adopt this plugin
83
+ by only warning about violations.
84
+
85
+ ```javascript
86
+ import friendsOfTheWeb from '@friendsoftheweb/eslint-plugin';
87
+ import { defineConfig } from 'eslint/config';
88
+ import tseslint from 'typescript-eslint';
89
+
90
+ export default defineConfig([
91
+ { ignores: ['.yarn/**/*'] },
92
+ {
93
+ files: ['**/*.{js,jsx,ts,tsx}'],
94
+ extends: [
95
+ tseslint.configs.recommended,
96
+ friendsOfTheWeb.configs['flat/migrate'],
97
+ ],
98
+ },
99
+ ]);
100
+ ```
101
+
102
+ ### Example with React
31
103
 
32
104
  ```javascript
33
105
  import friendsOfTheWeb from '@friendsoftheweb/eslint-plugin';
package/dist/cjs/index.js CHANGED
@@ -1,43 +1,357 @@
1
1
  'use strict';
2
2
 
3
- var cssModuleNameMatches = require('./rules/css-module-name-matches.js');
4
- var cssModuleClassExists = require('./rules/css-module-class-exists.js');
3
+ var path = require('node:path');
4
+ var fs = require('node:fs');
5
+ var postcss = require('postcss');
6
+ var selectorParser = require('postcss-selector-parser');
7
+
8
+ var name = "@friendsoftheweb/eslint-plugin";
9
+ var version = "0.0.2";
10
+ var packageJson = {
11
+ name: name,
12
+ version: version};
13
+
14
+ /** @type {import('eslint').JSRuleDefinition} */
15
+ var banLodashImport = {
16
+ meta: {
17
+ type: 'problem',
18
+ fixable: 'code',
19
+ docs: {
20
+ description: 'enforce importing functions from `lodash-es` instead of `lodash`',
21
+ url: 'https://github.com/friendsoftheweb/eslint-plugin#friendsofthewebban-lodash-import',
22
+ },
23
+ schema: [],
24
+ messages: {
25
+ invalidImport: 'Functions must be imported from "lodash-es" instead of "lodash"',
26
+ },
27
+ },
28
+ create(context) {
29
+ return {
30
+ ImportDeclaration(node) {
31
+ if (typeof node.source.value !== 'string' ||
32
+ (node.source.value !== 'lodash' &&
33
+ !node.source.value.startsWith('lodash/'))) {
34
+ return;
35
+ }
36
+ context.report({
37
+ node,
38
+ messageId: 'invalidImport',
39
+ fix(fixer) {
40
+ if (typeof node.source.value !== 'string') {
41
+ return null;
42
+ }
43
+ if (node.source.value === 'lodash/fp') {
44
+ return null; // no automatic fix for fp imports
45
+ }
46
+ const newImportPath = node.source.value === 'lodash'
47
+ ? 'lodash-es'
48
+ : node.source.value.replace(/^lodash\//, 'lodash-es/');
49
+ const quote = node.source.raw[0]; // preserve original quote style
50
+ return fixer.replaceText(node.source, `${quote}${newImportPath}${quote}`);
51
+ },
52
+ });
53
+ },
54
+ };
55
+ },
56
+ };
57
+
58
+ /** @type {import('eslint').JSRuleDefinition} */
59
+ var cssModuleNameMatchesRule = {
60
+ meta: {
61
+ type: 'problem',
62
+ docs: {
63
+ description: "enforce that a CSS module's filename matches the filename of the importing file",
64
+ url: 'https://github.com/friendsoftheweb/eslint-plugin#friendsofthewebcss-module-name-matches',
65
+ },
66
+ schema: [],
67
+ messages: {
68
+ filenameMismatch: 'CSS module filename "{{cssModuleFilename}}" does not match the current filename "{{filename}}"',
69
+ },
70
+ },
71
+ create(context) {
72
+ return {
73
+ ImportDeclaration(node) {
74
+ if (typeof node.source.value !== 'string' ||
75
+ !node.source.value.endsWith('.module.css')) {
76
+ return;
77
+ }
78
+ const filename = path.basename(context.filename, path.extname(context.filename));
79
+ const cssModulePath = node.source.value;
80
+ const cssModuleFilename = path.basename(cssModulePath, '.module.css');
81
+ if (cssModuleFilename !== filename) {
82
+ context.report({
83
+ node,
84
+ messageId: 'filenameMismatch',
85
+ data: {
86
+ cssModuleFilename,
87
+ filename,
88
+ },
89
+ });
90
+ }
91
+ },
92
+ };
93
+ },
94
+ };
95
+
96
+ /** @type {import('eslint').JSRuleDefinition} */
97
+ var cssModuleClassExistsRule = {
98
+ meta: {
99
+ type: 'problem',
100
+ docs: {
101
+ description: 'enforce that class names used from an imported CSS module exist in the module file',
102
+ url: 'https://github.com/friendsoftheweb/eslint-plugin#friendsofthewebcss-module-class-exists',
103
+ },
104
+ schema: [],
105
+ messages: {
106
+ relativePath: 'CSS module import "{{importPath}}" should be a relative path',
107
+ defaultImport: 'CSS module import "{{importPath}}" should have a default import',
108
+ onlyDefaultImport: 'CSS module import "{{importPath}}" should have only a default import',
109
+ fileDoesNotExist: 'CSS module file "{{absoluteImportPath}}" does not exist',
110
+ classDoesNotExist: 'Class `.{{className}}` does not exist in the CSS module imported as `{{objectName}}`',
111
+ },
112
+ },
113
+ create(context) {
114
+ const classNames = {};
115
+ return {
116
+ ImportDeclaration(node) {
117
+ if (typeof node.source.value !== 'string' ||
118
+ !node.source.value.endsWith('.module.css')) {
119
+ return;
120
+ }
121
+ const importPath = node.source.value;
122
+ if (!(importPath.startsWith('./') || importPath.startsWith('../'))) {
123
+ context.report({
124
+ node,
125
+ messageId: 'relativePath',
126
+ data: { importPath },
127
+ });
128
+ return;
129
+ }
130
+ if (node.specifiers.length === 0) {
131
+ context.report({
132
+ node,
133
+ messageId: 'defaultImport',
134
+ data: { importPath },
135
+ });
136
+ return;
137
+ }
138
+ if (node.specifiers.length > 1) {
139
+ context.report({
140
+ node,
141
+ messageId: 'onlyDefaultImport',
142
+ data: { importPath },
143
+ });
144
+ return;
145
+ }
146
+ const defaultImportSpecifier = node.specifiers.find((specifier) => specifier.type === 'ImportDefaultSpecifier');
147
+ if (defaultImportSpecifier == null) {
148
+ context.report({
149
+ node,
150
+ messageId: 'onlyDefaultImport',
151
+ data: { importPath },
152
+ });
153
+ return;
154
+ }
155
+ const dirname = path.dirname(context.physicalFilename);
156
+ const absoluteImportPath = path.resolve(dirname, importPath);
157
+ if (!fs.existsSync(absoluteImportPath)) {
158
+ context.report({
159
+ node,
160
+ messageId: 'fileDoesNotExist',
161
+ data: { absoluteImportPath },
162
+ });
163
+ return;
164
+ }
165
+ const importName = defaultImportSpecifier.local.name;
166
+ classNames[importName] = new Set();
167
+ const cssModuleContent = fs.readFileSync(absoluteImportPath, 'utf8');
168
+ const root = postcss.parse(cssModuleContent);
169
+ for (const node of root.nodes) {
170
+ if (node.type === 'rule') {
171
+ selectorParser(function transform(selectors) {
172
+ selectors.walkClasses((classNode) => {
173
+ classNames[importName].add(classNode.value);
174
+ });
175
+ }).processSync(node.selector);
176
+ }
177
+ else if (node.type === 'atrule' &&
178
+ (node.name === 'media' ||
179
+ node.name === 'container' ||
180
+ node.name === 'layer')) {
181
+ for (const childNode of node.nodes) {
182
+ if (childNode.type !== 'rule') {
183
+ continue;
184
+ }
185
+ selectorParser(function transform(selectors) {
186
+ selectors.walkClasses((classNode) => {
187
+ classNames[importName].add(classNode.value);
188
+ });
189
+ }).processSync(childNode.selector);
190
+ }
191
+ }
192
+ }
193
+ },
194
+ MemberExpression(node) {
195
+ if (node.object.type !== 'Identifier') {
196
+ return;
197
+ }
198
+ if (classNames[node.object.name] == null) {
199
+ return;
200
+ }
201
+ const objectName = node.object.name;
202
+ if (node.property.type === 'Identifier') {
203
+ const className = node.property.name;
204
+ if (!classNames[objectName].has(className)) {
205
+ context.report({
206
+ node,
207
+ messageId: 'classDoesNotExist',
208
+ data: { className, objectName },
209
+ });
210
+ }
211
+ return;
212
+ }
213
+ if (node.property.type === 'Literal' &&
214
+ typeof node.property.value === 'string') {
215
+ const className = node.property.value;
216
+ if (!classNames[objectName].has(className)) {
217
+ context.report({
218
+ node,
219
+ messageId: 'classDoesNotExist',
220
+ data: { className, objectName },
221
+ });
222
+ }
223
+ }
224
+ },
225
+ VariableDeclarator(node) {
226
+ var _a;
227
+ if (node.id.type !== 'ObjectPattern') {
228
+ return;
229
+ }
230
+ if (((_a = node.init) === null || _a === void 0 ? void 0 : _a.type) !== 'Identifier') {
231
+ return;
232
+ }
233
+ const objectName = node.init.name;
234
+ if (classNames[objectName] == null) {
235
+ return;
236
+ }
237
+ for (const property of node.id.properties) {
238
+ if (property.type !== 'Property') {
239
+ continue;
240
+ }
241
+ if (property.key.type !== 'Identifier') {
242
+ continue;
243
+ }
244
+ const className = property.key.name;
245
+ if (!classNames[objectName].has(className)) {
246
+ context.report({
247
+ node: property,
248
+ messageId: 'classDoesNotExist',
249
+ data: { className, objectName },
250
+ });
251
+ }
252
+ }
253
+ },
254
+ };
255
+ },
256
+ };
257
+
258
+ /** @type {import('eslint').JSRuleDefinition} */
259
+ var validServerActionsPathRule = {
260
+ meta: {
261
+ type: 'problem',
262
+ docs: {
263
+ description: 'enforce server actions are exported from file paths that match "app/**/_actions.ts" or "app/**/_actions/**/*.ts"',
264
+ url: 'https://github.com/friendsoftheweb/eslint-plugin#friendsofthewebvalid-server-actions-path',
265
+ },
266
+ schema: [],
267
+ messages: {
268
+ invalidPath: 'Server action files must be located in a directory named "_actions" or have the filename "_actions.ts"',
269
+ },
270
+ },
271
+ create(context) {
272
+ return {
273
+ ExpressionStatement(node) {
274
+ if (node.expression.type !== 'Literal' ||
275
+ node.expression.value !== 'use server') {
276
+ return;
277
+ }
278
+ const basename = path.basename(context.filename);
279
+ const dirname = path.dirname(context.filename);
280
+ // Escape backslashes for RegExp (Windows paths)
281
+ const escapedSep = path.sep.replace('\\', '\\\\');
282
+ const isInActionsDir = new RegExp(`app(${escapedSep}.*)?${escapedSep}_actions`).test(dirname);
283
+ const isActionsFile = (dirname === 'app' || new RegExp(`app${escapedSep}`).test(dirname)) &&
284
+ /_actions\.(js|ts)$/.test(basename);
285
+ if (!isInActionsDir && !isActionsFile) {
286
+ context.report({
287
+ node,
288
+ messageId: 'invalidPath',
289
+ });
290
+ }
291
+ },
292
+ };
293
+ },
294
+ };
5
295
 
6
296
  /** @type {import('eslint').ESLint.Plugin} */
7
297
  const plugin = {
8
- meta: {
9
- name: '@friendsoftheweb/eslint-plugin',
10
- version: '0.0.1',
11
- },
12
- configs: {},
13
- rules: {
14
- 'css-module-name-matches': cssModuleNameMatches,
15
- 'css-module-class-exists': cssModuleClassExists,
16
- },
298
+ meta: {
299
+ name: packageJson.name,
300
+ version: packageJson.version,
301
+ },
302
+ configs: {},
303
+ rules: {
304
+ 'ban-lodash-import': banLodashImport,
305
+ 'css-module-name-matches': cssModuleNameMatchesRule,
306
+ 'css-module-class-exists': cssModuleClassExistsRule,
307
+ 'valid-server-actions-path': validServerActionsPathRule,
308
+ },
17
309
  };
18
-
19
310
  Object.assign(plugin.configs, {
20
- // flat config format
21
- 'flat/recommended': [
22
- {
23
- plugins: {
24
- friendsoftheweb: plugin,
25
- },
26
- rules: {
27
- 'friendsoftheweb/css-module-name-matches': 'error',
28
- 'friendsoftheweb/css-module-class-exists': 'error',
29
- },
30
- },
31
- ],
32
-
33
- // eslintrc format
34
- recommended: {
35
- plugins: { friendsoftheweb: plugin },
36
- rules: {
37
- 'friendsoftheweb/css-module-name-matches': 'error',
38
- 'friendsoftheweb/css-module-class-exists': 'error',
311
+ 'flat/recommended': [
312
+ {
313
+ plugins: {
314
+ friendsoftheweb: plugin,
315
+ },
316
+ rules: {
317
+ 'friendsoftheweb/ban-lodash-import': 'error',
318
+ 'friendsoftheweb/css-module-name-matches': 'error',
319
+ 'friendsoftheweb/css-module-class-exists': 'error',
320
+ 'friendsoftheweb/valid-server-actions-path': 'error',
321
+ },
322
+ },
323
+ ],
324
+ 'flat/migrate': [
325
+ {
326
+ plugins: {
327
+ friendsoftheweb: plugin,
328
+ },
329
+ rules: {
330
+ 'friendsoftheweb/ban-lodash-import': 'warn',
331
+ 'friendsoftheweb/css-module-name-matches': 'warn',
332
+ 'friendsoftheweb/css-module-class-exists': 'warn',
333
+ 'friendsoftheweb/valid-server-actions-path': 'warn',
334
+ },
335
+ },
336
+ ],
337
+ recommended: {
338
+ plugins: { friendsoftheweb: plugin },
339
+ rules: {
340
+ 'friendsoftheweb/ban-lodash-import': 'error',
341
+ 'friendsoftheweb/css-module-name-matches': 'error',
342
+ 'friendsoftheweb/css-module-class-exists': 'error',
343
+ 'friendsoftheweb/valid-server-actions-path': 'error',
344
+ },
345
+ },
346
+ migrate: {
347
+ plugins: { friendsoftheweb: plugin },
348
+ rules: {
349
+ 'friendsoftheweb/ban-lodash-import': 'warn',
350
+ 'friendsoftheweb/css-module-name-matches': 'warn',
351
+ 'friendsoftheweb/css-module-class-exists': 'warn',
352
+ 'friendsoftheweb/valid-server-actions-path': 'warn',
353
+ },
39
354
  },
40
- },
41
355
  });
42
356
 
43
357
  module.exports = plugin;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/index.mjs"],"sourcesContent":["import cssModuleNameMatchesRule from './rules/css-module-name-matches.mjs';\nimport cssModuleClassExistsRule from './rules/css-module-class-exists.mjs';\n\n/** @type {import('eslint').ESLint.Plugin} */\nconst plugin = {\n meta: {\n name: '@friendsoftheweb/eslint-plugin',\n version: '0.0.1',\n },\n configs: {},\n rules: {\n 'css-module-name-matches': cssModuleNameMatchesRule,\n 'css-module-class-exists': cssModuleClassExistsRule,\n },\n};\n\nObject.assign(plugin.configs, {\n // flat config format\n 'flat/recommended': [\n {\n plugins: {\n friendsoftheweb: plugin,\n },\n rules: {\n 'friendsoftheweb/css-module-name-matches': 'error',\n 'friendsoftheweb/css-module-class-exists': 'error',\n },\n },\n ],\n\n // eslintrc format\n recommended: {\n plugins: { friendsoftheweb: plugin },\n rules: {\n 'friendsoftheweb/css-module-name-matches': 'error',\n 'friendsoftheweb/css-module-class-exists': 'error',\n },\n },\n});\n\nexport default plugin;\n"],"names":["cssModuleNameMatchesRule","cssModuleClassExistsRule"],"mappings":";;;;;AAGA;AACK,MAAC,MAAM,GAAG;AACf,EAAE,IAAI,EAAE;AACR,IAAI,IAAI,EAAE,gCAAgC;AAC1C,IAAI,OAAO,EAAE,OAAO;AACpB,GAAG;AACH,EAAE,OAAO,EAAE,EAAE;AACb,EAAE,KAAK,EAAE;AACT,IAAI,yBAAyB,EAAEA,oBAAwB;AACvD,IAAI,yBAAyB,EAAEC,oBAAwB;AACvD,GAAG;AACH;;AAEA,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE;AAC9B;AACA,EAAE,kBAAkB,EAAE;AACtB,IAAI;AACJ,MAAM,OAAO,EAAE;AACf,QAAQ,eAAe,EAAE,MAAM;AAC/B,OAAO;AACP,MAAM,KAAK,EAAE;AACb,QAAQ,yCAAyC,EAAE,OAAO;AAC1D,QAAQ,yCAAyC,EAAE,OAAO;AAC1D,OAAO;AACP,KAAK;AACL,GAAG;;AAEH;AACA,EAAE,WAAW,EAAE;AACf,IAAI,OAAO,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE;AACxC,IAAI,KAAK,EAAE;AACX,MAAM,yCAAyC,EAAE,OAAO;AACxD,MAAM,yCAAyC,EAAE,OAAO;AACxD,KAAK;AACL,GAAG;AACH,CAAC,CAAC;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/rules/ban-lodash-import.mjs","../../../../src/rules/css-module-name-matches.mjs","../../../../src/rules/css-module-class-exists.mjs","../../../../src/rules/valid-server-actions-path.mjs","../../../../src/index.mjs"],"sourcesContent":["/** @type {import('eslint').JSRuleDefinition} */\nexport default {\n meta: {\n type: 'problem',\n fixable: 'code',\n docs: {\n description:\n 'enforce importing functions from `lodash-es` instead of `lodash`',\n url: 'https://github.com/friendsoftheweb/eslint-plugin#friendsofthewebban-lodash-import',\n },\n schema: [],\n messages: {\n invalidImport:\n 'Functions must be imported from \"lodash-es\" instead of \"lodash\"',\n },\n },\n create(context) {\n return {\n ImportDeclaration(node) {\n if (\n typeof node.source.value !== 'string' ||\n (node.source.value !== 'lodash' &&\n !node.source.value.startsWith('lodash/'))\n ) {\n return;\n }\n\n context.report({\n node,\n messageId: 'invalidImport',\n fix(fixer) {\n if (typeof node.source.value !== 'string') {\n return null;\n }\n\n if (node.source.value === 'lodash/fp') {\n return null; // no automatic fix for fp imports\n }\n\n const newImportPath =\n node.source.value === 'lodash'\n ? 'lodash-es'\n : node.source.value.replace(/^lodash\\//, 'lodash-es/');\n\n const quote = node.source.raw[0]; // preserve original quote style\n\n return fixer.replaceText(\n node.source,\n `${quote}${newImportPath}${quote}`,\n );\n },\n });\n },\n };\n },\n};\n","import path from 'node:path';\n\n/** @type {import('eslint').JSRuleDefinition} */\nexport default {\n meta: {\n type: 'problem',\n docs: {\n description:\n \"enforce that a CSS module's filename matches the filename of the importing file\",\n url: 'https://github.com/friendsoftheweb/eslint-plugin#friendsofthewebcss-module-name-matches',\n },\n schema: [],\n messages: {\n filenameMismatch:\n 'CSS module filename \"{{cssModuleFilename}}\" does not match the current filename \"{{filename}}\"',\n },\n },\n create(context) {\n return {\n ImportDeclaration(node) {\n if (\n typeof node.source.value !== 'string' ||\n !node.source.value.endsWith('.module.css')\n ) {\n return;\n }\n\n const filename = path.basename(\n context.filename,\n path.extname(context.filename),\n );\n\n const cssModulePath = node.source.value;\n const cssModuleFilename = path.basename(cssModulePath, '.module.css');\n\n if (cssModuleFilename !== filename) {\n context.report({\n node,\n messageId: 'filenameMismatch',\n data: {\n cssModuleFilename,\n filename,\n },\n });\n }\n },\n };\n },\n};\n","import path from 'node:path';\nimport fs from 'node:fs';\n\nimport { parse } from 'postcss';\nimport selectorParser from 'postcss-selector-parser';\n\n/** @type {import('eslint').JSRuleDefinition} */\nexport default {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'enforce that class names used from an imported CSS module exist in the module file',\n url: 'https://github.com/friendsoftheweb/eslint-plugin#friendsofthewebcss-module-class-exists',\n },\n schema: [],\n messages: {\n relativePath:\n 'CSS module import \"{{importPath}}\" should be a relative path',\n defaultImport:\n 'CSS module import \"{{importPath}}\" should have a default import',\n onlyDefaultImport:\n 'CSS module import \"{{importPath}}\" should have only a default import',\n fileDoesNotExist:\n 'CSS module file \"{{absoluteImportPath}}\" does not exist',\n classDoesNotExist:\n 'Class `.{{className}}` does not exist in the CSS module imported as `{{objectName}}`',\n },\n },\n create(context) {\n const classNames = {};\n\n return {\n ImportDeclaration(node) {\n if (\n typeof node.source.value !== 'string' ||\n !node.source.value.endsWith('.module.css')\n ) {\n return;\n }\n\n const importPath = node.source.value;\n\n if (!(importPath.startsWith('./') || importPath.startsWith('../'))) {\n context.report({\n node,\n messageId: 'relativePath',\n data: { importPath },\n });\n\n return;\n }\n\n if (node.specifiers.length === 0) {\n context.report({\n node,\n messageId: 'defaultImport',\n data: { importPath },\n });\n\n return;\n }\n\n if (node.specifiers.length > 1) {\n context.report({\n node,\n messageId: 'onlyDefaultImport',\n data: { importPath },\n });\n\n return;\n }\n\n const defaultImportSpecifier = node.specifiers.find(\n (specifier) => specifier.type === 'ImportDefaultSpecifier',\n );\n\n if (defaultImportSpecifier == null) {\n context.report({\n node,\n messageId: 'onlyDefaultImport',\n data: { importPath },\n });\n\n return;\n }\n\n const dirname = path.dirname(context.physicalFilename);\n const absoluteImportPath = path.resolve(dirname, importPath);\n\n if (!fs.existsSync(absoluteImportPath)) {\n context.report({\n node,\n messageId: 'fileDoesNotExist',\n data: { absoluteImportPath },\n });\n\n return;\n }\n\n const importName = defaultImportSpecifier.local.name;\n\n classNames[importName] = new Set();\n\n const cssModuleContent = fs.readFileSync(absoluteImportPath, 'utf8');\n const root = parse(cssModuleContent);\n\n for (const node of root.nodes) {\n if (node.type === 'rule') {\n selectorParser(function transform(selectors) {\n selectors.walkClasses((classNode) => {\n classNames[importName].add(classNode.value);\n });\n }).processSync(node.selector);\n } else if (\n node.type === 'atrule' &&\n (node.name === 'media' ||\n node.name === 'container' ||\n node.name === 'layer')\n ) {\n for (const childNode of node.nodes) {\n if (childNode.type !== 'rule') {\n continue;\n }\n\n selectorParser(function transform(selectors) {\n selectors.walkClasses((classNode) => {\n classNames[importName].add(classNode.value);\n });\n }).processSync(childNode.selector);\n }\n }\n }\n },\n MemberExpression(node) {\n if (node.object.type !== 'Identifier') {\n return;\n }\n\n if (classNames[node.object.name] == null) {\n return;\n }\n\n const objectName = node.object.name;\n\n if (node.property.type === 'Identifier') {\n const className = node.property.name;\n\n if (!classNames[objectName].has(className)) {\n context.report({\n node,\n messageId: 'classDoesNotExist',\n data: { className, objectName },\n });\n }\n\n return;\n }\n\n if (\n node.property.type === 'Literal' &&\n typeof node.property.value === 'string'\n ) {\n const className = node.property.value;\n\n if (!classNames[objectName].has(className)) {\n context.report({\n node,\n messageId: 'classDoesNotExist',\n data: { className, objectName },\n });\n }\n }\n },\n VariableDeclarator(node) {\n if (node.id.type !== 'ObjectPattern') {\n return;\n }\n\n if (node.init?.type !== 'Identifier') {\n return;\n }\n\n const objectName = node.init.name;\n\n if (classNames[objectName] == null) {\n return;\n }\n\n for (const property of node.id.properties) {\n if (property.type !== 'Property') {\n continue;\n }\n\n if (property.key.type !== 'Identifier') {\n continue;\n }\n\n const className = property.key.name;\n\n if (!classNames[objectName].has(className)) {\n context.report({\n node: property,\n messageId: 'classDoesNotExist',\n data: { className, objectName },\n });\n }\n }\n },\n };\n },\n};\n","import path from 'node:path';\n\n/** @type {import('eslint').JSRuleDefinition} */\nexport default {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'enforce server actions are exported from file paths that match \"app/**/_actions.ts\" or \"app/**/_actions/**/*.ts\"',\n url: 'https://github.com/friendsoftheweb/eslint-plugin#friendsofthewebvalid-server-actions-path',\n },\n schema: [],\n messages: {\n invalidPath:\n 'Server action files must be located in a directory named \"_actions\" or have the filename \"_actions.ts\"',\n },\n },\n create(context) {\n return {\n ExpressionStatement(node) {\n if (\n node.expression.type !== 'Literal' ||\n node.expression.value !== 'use server'\n ) {\n return;\n }\n\n const basename = path.basename(context.filename);\n const dirname = path.dirname(context.filename);\n\n // Escape backslashes for RegExp (Windows paths)\n const escapedSep = path.sep.replace('\\\\', '\\\\\\\\');\n\n const isInActionsDir = new RegExp(\n `app(${escapedSep}.*)?${escapedSep}_actions`,\n ).test(dirname);\n\n const isActionsFile =\n (dirname === 'app' || new RegExp(`app${escapedSep}`).test(dirname)) &&\n /_actions\\.(js|ts)$/.test(basename);\n\n if (!isInActionsDir && !isActionsFile) {\n context.report({\n node,\n messageId: 'invalidPath',\n });\n }\n },\n };\n },\n};\n","import packageJson from '../package.json' with { type: 'json' };\n\nimport banLodashImport from './rules/ban-lodash-import.mjs';\nimport cssModuleNameMatchesRule from './rules/css-module-name-matches.mjs';\nimport cssModuleClassExistsRule from './rules/css-module-class-exists.mjs';\nimport validServerActionsPathRule from './rules/valid-server-actions-path.mjs';\n\n/** @type {import('eslint').ESLint.Plugin} */\nconst plugin = {\n meta: {\n name: packageJson.name,\n version: packageJson.version,\n },\n configs: {},\n rules: {\n 'ban-lodash-import': banLodashImport,\n 'css-module-name-matches': cssModuleNameMatchesRule,\n 'css-module-class-exists': cssModuleClassExistsRule,\n 'valid-server-actions-path': validServerActionsPathRule,\n },\n};\n\nObject.assign(plugin.configs, {\n 'flat/recommended': [\n {\n plugins: {\n friendsoftheweb: plugin,\n },\n rules: {\n 'friendsoftheweb/ban-lodash-import': 'error',\n 'friendsoftheweb/css-module-name-matches': 'error',\n 'friendsoftheweb/css-module-class-exists': 'error',\n 'friendsoftheweb/valid-server-actions-path': 'error',\n },\n },\n ],\n\n 'flat/migrate': [\n {\n plugins: {\n friendsoftheweb: plugin,\n },\n rules: {\n 'friendsoftheweb/ban-lodash-import': 'warn',\n 'friendsoftheweb/css-module-name-matches': 'warn',\n 'friendsoftheweb/css-module-class-exists': 'warn',\n 'friendsoftheweb/valid-server-actions-path': 'warn',\n },\n },\n ],\n\n recommended: {\n plugins: { friendsoftheweb: plugin },\n rules: {\n 'friendsoftheweb/ban-lodash-import': 'error',\n 'friendsoftheweb/css-module-name-matches': 'error',\n 'friendsoftheweb/css-module-class-exists': 'error',\n 'friendsoftheweb/valid-server-actions-path': 'error',\n },\n },\n\n migrate: {\n plugins: { friendsoftheweb: plugin },\n rules: {\n 'friendsoftheweb/ban-lodash-import': 'warn',\n 'friendsoftheweb/css-module-name-matches': 'warn',\n 'friendsoftheweb/css-module-class-exists': 'warn',\n 'friendsoftheweb/valid-server-actions-path': 'warn',\n },\n },\n});\n\nexport default plugin;\n"],"names":["parse"],"mappings":"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"}