Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ESLint Plugin] Enforce inclusion of dist-esm and exclusion of src in files field in package.json #11411

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Requires `files` in `package.json` to contain paths to the package contents.

Specifically, this rule looks for inclusion of `dist`, `dist-esm/src`, and `src` as either just those directories or specific subdirectories
Specifically, this rule looks for inclusion of `dist`, and the exclusion of `dist-esm/src`, and `src` as either just those directories or specific subdirectories

This rule is fixable using the `--fix` option.

Expand All @@ -12,41 +12,25 @@ This rule is fixable using the `--fix` option.

```json
{
"files": [
"dist",
"dist-esm/src"
"src"
]
"files": ["dist"]
}
```

```json
{
"files": [
"./dist",
"./dist-esm/src"
"./src"
]
"files": ["./dist"]
}
```

```json
{
"files": [
"dist/",
"dist-esm/src/"
"src/"
]
"files": ["dist/"]
}
```

```json
{
"files": [
"dist/lib",
"dist-esm/src/lib"
"src/lib"
]
"files": ["dist/lib"]
}
```

Expand All @@ -58,12 +42,6 @@ This rule is fixable using the `--fix` option.
}
```

```json
{
"files": ["dist"]
}
```

```json
{
"files": []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT license.

/**
* @file Rule to force package.json's files value to contain paths to the package contents.
* @file Rule to force package.json's files value to contain paths to the package contents and excludes source code files.
* @author Arpan Laha
*/

Expand Down Expand Up @@ -31,7 +31,7 @@ export = {
// check to see if files exists at the outermost level
"ExpressionStatement > ObjectExpression": verifiers.existsInFile,

// check that files contains dist, dist-esm, and src
// check that files contains dist, and do not include dist-esm, and src
"ExpressionStatement > ObjectExpression > Property[key.value='files']": (
node: Property
): void => {
Expand All @@ -46,37 +46,60 @@ export = {

const nodeValue = node.value;
const elements = nodeValue.elements as Literal[];
const elementValues = elements.map((element: Literal): unknown => element.value);
let elementValues = elements.map((element: Literal): unknown => element.value);

// looks for 'dist' with optional leading './' and optional trailing '/'
if (
elements.every(
(element: Literal): boolean =>
!/^(.\/)?((dist\/)|(dist$))/.test(element.value as string)
)
) {
context.report({
node: nodeValue,
message: "dist is not included in files",
fix: (fixer: Rule.RuleFixer): Rule.Fix => {
elementValues.push("dist");
return fixer.replaceText(nodeValue, arrayToString(elementValues));
let badFiles: string[] = [];
let goodFiles = ["dist"];
const fullMatchIndex = 0;
const patternRootMatchIndex = 1;
elements.forEach((element) => {
deyaaeldeen marked this conversation as resolved.
Show resolved Hide resolved
const patternMatchResult = (element.value as string).match(
/^(?:.\/)?(dist-esm|dist|src)(?:\/)?(?:.+)?/
);
if (patternMatchResult !== null) {
const patternRoot = patternMatchResult[patternRootMatchIndex];
switch (patternRoot) {
case "dist-esm":
case "src":
badFiles.push(patternMatchResult[fullMatchIndex]);
break;
case "dist":
goodFiles.splice(goodFiles.indexOf(patternRoot));
break;
default:
context.report({
node: nodeValue,
message: "impossible"
});
return;
}
});
}
});
let message = "";
if (badFiles.length > 0) {
message = `${badFiles.join()} ${
badFiles.length === 1 ? "is" : "are"
} included in files`;
elementValues = elementValues.filter(
(element) => badFiles.indexOf(element as string) < 0
);
}

// looks for 'dist-esm/src' with optional leading './' and optional trailing '/'
if (
elements.every(
(element: Literal): boolean =>
!/^(.\/)?dist-esm\/((src\/)|(src$))/.test(element.value as string)
)
) {
if (goodFiles.length > 0) {
if (message.length > 0) {
message = message + " and ";
}
message =
message +
`${goodFiles.join()} ${
goodFiles.length === 1 ? "is" : "are"
} not included in files`;
elementValues = elementValues.concat(goodFiles);
}
if (message.length > 0) {
context.report({
node: nodeValue,
message: "dist-esm/src is not included in files",
message: message,
fix: (fixer: Rule.RuleFixer): Rule.Fix => {
elementValues.push("dist-esm/src");
return fixer.replaceText(nodeValue, arrayToString(elementValues));
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,7 @@ const examplePackageGood = `{
"files": [
"typings/service-bus.d.ts",
"tsconfig.json",
"dist",
"dist-esm/src"
"dist"
],
"sideEffects": false
}`;
Expand Down Expand Up @@ -230,7 +229,8 @@ const examplePackageBad = `{
},
"files": [
"typings/service-bus.d.ts",
"tsconfig.json"
"tsconfig.json",
"dist-esm/src"
],
"sideEffects": false
}`;
Expand All @@ -251,25 +251,20 @@ ruleTester.run("ts-package-json-files-required", rule, {
valid: [
{
// only the fields we care about
code: '{"files": ["dist", "dist-esm/src"]}',
code: '{"files": ["dist"]}',
filename: "package.json"
},
// other valid formats
{
code: '{"files": ["dist/", "dist-esm/src/"]}',
filename: "package.json"
},
{
code: '{"files": ["./dist", "./dist-esm/src"]}',
code: '{"files": ["dist/"]}',
filename: "package.json"
},
{
code: '{"files": ["./dist/", "./dist-esm/src/"]}',
code: '{"files": ["./dist"]}',
filename: "package.json"
},
{
// mixed
code: '{"files": ["dist/", "./dist-esm/src/"]}',
code: '{"files": ["./dist/"]}',
filename: "package.json"
},
{
Expand All @@ -295,7 +290,7 @@ ruleTester.run("ts-package-json-files-required", rule, {
},
{
// name is in a nested object
code: '{"outer": {"files": ["dist", "dist-esm/src"]}}',
code: '{"outer": {"files": ["dist"]}}',
filename: "package.json",
errors: [
{
Expand All @@ -309,50 +304,37 @@ ruleTester.run("ts-package-json-files-required", rule, {
filename: "package.json",
errors: [
{
message: "dist-esm/src is not included in files"
message: "src is included in files"
}
],
output: '{"files": ["dist", "src", "dist-esm/src"]}'
output: '{"files": ["dist"]}'
},
{
code: '{"files": ["src", "dist-esm/src"]}',
code: '{"files": ["types/package.d.ts"]}',
filename: "package.json",
errors: [
{
message: "dist is not included in files"
}
],
output: '{"files": ["src", "dist-esm/src", "dist"]}'
output: '{"files": ["types/package.d.ts", "dist"]}'
},
{
code: '{"files": ["dist"]}',
code: '{"files": ["dist", "src", "dist-esm/src"]}',
filename: "package.json",
errors: [
{
message: "dist-esm/src is not included in files"
message: "src,dist-esm/src are included in files"
}
],
output: '{"files": ["dist", "dist-esm/src"]}'
output: '{"files": ["dist"]}'
},
{
code: '{"files": ["dist-esm/src"]}',
filename: "package.json",
errors: [
{
message: "dist is not included in files"
}
],
output: '{"files": ["dist-esm/src", "dist"]}'
},
{
code: '{"files": []}',
filename: "package.json",
errors: [
{
message: "dist is not included in files"
},
{
message: "dist-esm/src is not included in files"
message: "dist-esm/src is included in files and dist is not included in files"
}
],
output: '{"files": ["dist"]}'
Expand All @@ -363,10 +345,7 @@ ruleTester.run("ts-package-json-files-required", rule, {
filename: "package.json",
errors: [
{
message: "dist is not included in files"
},
{
message: "dist-esm/src is not included in files"
message: "dist-esm/src is included in files and dist is not included in files"
}
]
}
Expand Down