Skip to content

Commit

Permalink
feat: update filename-* rules to use options object
Browse files Browse the repository at this point in the history
BREAKING CHANGE: filename- rules now expect options object.
  • Loading branch information
gajus committed Feb 8, 2023
1 parent affde54 commit 3283274
Show file tree
Hide file tree
Showing 20 changed files with 252 additions and 192 deletions.
16 changes: 8 additions & 8 deletions .README/rules/filename-match-exported.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default { foo: "bar" };
If your filename policy doesn't quite match with your variable naming policy, you can add one or multiple transforms:

```json
"canonical/filename-match-exported": [ 2, "kebab" ]
"canonical/filename-match-exported": [ 2, { "transforms": "kebab" } ]
```

Now, in your code:
Expand All @@ -31,21 +31,21 @@ export default function variableName;

Available transforms:

* snake
* kebab
* camel
* pascal
* `snake`
* `kebab`
* `camel`
* `pascal`

For multiple transforms simply specify an array like this (null in this case stands for no transform):

```json
"canonical/filename-match-exported": [2, [ null, "kebab", "snake" ] ]
"canonical/filename-match-exported": [2, { "transforms": [ null, "kebab", "snake" ] } ]
```

If you prefer to use suffixes for your files (e.g. `Foo.react.js` for a React component file), you can use a second configuration parameter. It allows you to remove parts of a filename matching a regex pattern before transforming and matching against the export.

```json
"canonical/filename-match-exported": [ 2, null, "\\.react$" ]
"canonical/filename-match-exported": [ 2, { "suffix": "\\.react$" } ]
```

Now, in your code:
Expand All @@ -58,7 +58,7 @@ export default function variableName;
If you also want to match exported function calls you can use the third option (a boolean flag).

```json
"canonical/filename-match-exported": [ 2, null, null, true ]
"canonical/filename-match-exported": [ 2, { "matchCallExpression": true } ]
```

Now, in your code:
Expand Down
2 changes: 1 addition & 1 deletion .README/rules/filename-match-regex.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The convention can be configured using a regular expression (the default is `cam
exporting files can be ignored with a second configuration parameter.

```json
"canonical/filename-match-regex": [2, "^[a-z_]+$", true]
"canonical/filename-match-regex": [2, { "regex": "^[a-z_]+$", "ignoreExporting": true }]
```

With these configuration options, `camelCase.js` will be reported as an error while `snake_case.js` will pass.
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export default { foo: "bar" };
If your filename policy doesn't quite match with your variable naming policy, you can add one or multiple transforms:

```json
"canonical/filename-match-exported": [ 2, "kebab" ]
"canonical/filename-match-exported": [ 2, { "transforms": "kebab" } ]
```

Now, in your code:
Expand All @@ -168,21 +168,21 @@ export default function variableName;

Available transforms:

* snake
* kebab
* camel
* pascal
* `snake`
* `kebab`
* `camel`
* `pascal`

For multiple transforms simply specify an array like this (null in this case stands for no transform):

```json
"canonical/filename-match-exported": [2, [ null, "kebab", "snake" ] ]
"canonical/filename-match-exported": [2, { "transforms": [ null, "kebab", "snake" ] } ]
```

If you prefer to use suffixes for your files (e.g. `Foo.react.js` for a React component file), you can use a second configuration parameter. It allows you to remove parts of a filename matching a regex pattern before transforming and matching against the export.

```json
"canonical/filename-match-exported": [ 2, null, "\\.react$" ]
"canonical/filename-match-exported": [ 2, { "suffix": "\\.react$" } ]
```

Now, in your code:
Expand All @@ -195,7 +195,7 @@ export default function variableName;
If you also want to match exported function calls you can use the third option (a boolean flag).

```json
"canonical/filename-match-exported": [ 2, null, null, true ]
"canonical/filename-match-exported": [ 2, { "matchCallExpression": true } ]
```

Now, in your code:
Expand Down
14 changes: 10 additions & 4 deletions src/rules/destructuringPropertyNewline.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { createRule } from '../utilities';

export default createRule({
create(context) {
const allowAllPropertiesOnSameLine =
context.options[0]?.allowAllPropertiesOnSameLine;
type Options = [
{
allowAllPropertiesOnSameLine: boolean;
},
];

type MessageIds = 'propertiesOnNewline' | 'propertiesOnNewlineAll';

export default createRule<Options, MessageIds>({
create(context, [{ allowAllPropertiesOnSameLine }]) {
const messageId = allowAllPropertiesOnSameLine
? 'propertiesOnNewlineAll'
: 'propertiesOnNewline';
Expand Down
6 changes: 5 additions & 1 deletion src/rules/exportSpecifierNewline.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { createRule } from '../utilities';

export default createRule({
type Options = [];

type MessageIds = 'specifiersOnNewline';

export default createRule<Options, MessageIds>({
create: (context) => {
return {
ExportNamedDeclaration: (node) => {
Expand Down
182 changes: 94 additions & 88 deletions src/rules/filenameMatchExported.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,108 +64,114 @@ const getWhatToMatchMessage = (transforms) => {
return 'the exported and transformed name';
};

const create = (context) => {
return {
Program(node) {
const transforms = getTransformsFromOptions(context.options[0]);
const replacePattern = context.options[1]
? new RegExp(context.options[1], 'u')
: undefined;
const filename = context.getFilename();
const absoluteFilename = path.resolve(filename);
const parsed = parseFilename(absoluteFilename);
const shouldIgnore = isIgnoredFilename(filename);
const exportedName = getExportedName(node, context.options);
const isExporting = Boolean(exportedName);
const expectedExport = getStringToCheckAgainstExport(
parsed,
replacePattern,
);
const transformedNames = transform(exportedName, transforms);
const everythingIsIndex =
exportedName === 'index' && parsed.name === 'index';
const matchesExported =
anyMatch(expectedExport, transformedNames) || everythingIsIndex;
const reportIf = function (
condition,
messageForNormalFile,
messageForIndexFile,
) {
const message =
!messageForIndexFile || !isIndexFile(parsed)
? messageForNormalFile
: messageForIndexFile;

if (condition) {
context.report({
data: {
expectedExport,
exportName: transformedNames.join("', '"),
extension: parsed.ext,
name: parsed.base,
whatToMatch: getWhatToMatchMessage(transforms),
},
message,
node,
});
type Options = [
{
matchCallExpression: boolean;
suffix: string | null;
transforms: string[] | string | null;
},
];

type MessageIds = 'indexFile' | 'regularFile';

export default createRule<Options, MessageIds>({
create: (context, options) => {
return {
Program(node) {
const transforms = getTransformsFromOptions(options[0].transforms);
const replacePattern = options[0].suffix
? new RegExp(options[0].suffix, 'u')
: undefined;
const filename = context.getFilename();
const absoluteFilename = path.resolve(filename);
const parsed = parseFilename(absoluteFilename);
const shouldIgnore = isIgnoredFilename(filename);
const exportedName = getExportedName(
node,
options[0].matchCallExpression,
);
const isExporting = Boolean(exportedName);
const expectedExport = getStringToCheckAgainstExport(
parsed,
replacePattern,
);
const transformedNames = transform(exportedName, transforms);
const everythingIsIndex =
exportedName === 'index' && parsed.name === 'index';
const matchesExported =
anyMatch(expectedExport, transformedNames) || everythingIsIndex;

if (shouldIgnore || !isExporting || matchesExported) {
return;
}
};

if (shouldIgnore) {
return;
}

reportIf(
isExporting && !matchesExported,
"Filename '{{expectedExport}}' must match {{whatToMatch}} '{{exportName}}'.",
"The directory '{{expectedExport}}' must be named '{{exportName}}', after the exported value of its index file.",
);
context.report({
data: {
expectedExport,
exportName: transformedNames.join("', '"),
extension: parsed.ext,
name: parsed.base,
whatToMatch: getWhatToMatchMessage(transforms),
},
messageId: isIndexFile(parsed) ? 'indexFile' : 'regularFile',
node,
});
},
};
},
defaultOptions: [
{
matchCallExpression: false,
suffix: null,
transforms: null,
},
};
};

export default createRule({
create,
defaultOptions: [],
],
meta: {
docs: {
description:
'Match the file name against the default exported value in the module.',
recommended: 'warn',
},
messages: {},
messages: {
indexFile:
"The directory '{{expectedExport}}' must be named '{{exportName}}', after the exported value of its index file.",
regularFile:
"Filename '{{expectedExport}}' must match {{whatToMatch}} '{{exportName}}'.",
},
schema: [
{
anyOf: [
{
items: {
type: 'string',
},
type: 'array',
properties: {
matchCallExpression: {
type: 'boolean',
},
{
type: 'string',
suffix: {
oneOf: [
{
type: 'string',
},
{
type: 'null',
},
],
},
{
type: 'null',
transforms: {
oneOf: [
{
items: {
type: 'string',
},
type: 'array',
},
{
type: 'string',
},
{
type: 'null',
},
],
},
],
default: null,
},
{
default: null,
oneOf: [
{
type: 'string',
},
{
type: 'null',
},
],
},
{
default: false,
type: 'boolean',
},
type: 'object',
},
],
type: 'suggestion',
Expand Down
Loading

0 comments on commit 3283274

Please sign in to comment.