@slip-stream-kit/eslint-plugin 0.44.0 → 0.45.0
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/dist/index.js
CHANGED
|
@@ -4348,6 +4348,235 @@ var requireJsdocExample = {
|
|
|
4348
4348
|
}
|
|
4349
4349
|
};
|
|
4350
4350
|
|
|
4351
|
+
// src/rules/services-namespace-import/services-namespace-import.ts
|
|
4352
|
+
var DEFAULT_NAME = "service";
|
|
4353
|
+
var BARREL_FILE = /\/index\.[cm]?[jt]sx?$/;
|
|
4354
|
+
var SERVICES_SPECIFIER = /^\.{1,2}\/(?:.*\/)?services(?:\/index)?(?:\.[cm]?[jt]sx?)?$/;
|
|
4355
|
+
var isTypeOnly = (node) => {
|
|
4356
|
+
return node.importKind === "type";
|
|
4357
|
+
};
|
|
4358
|
+
var isValueBinding = (specifier) => {
|
|
4359
|
+
return specifier.type !== "ImportNamespaceSpecifier" && !isTypeOnly(specifier);
|
|
4360
|
+
};
|
|
4361
|
+
var importedName = (specifier) => {
|
|
4362
|
+
if (specifier.type === "ImportDefaultSpecifier") {
|
|
4363
|
+
return "default";
|
|
4364
|
+
}
|
|
4365
|
+
if (specifier.type === "ImportSpecifier") {
|
|
4366
|
+
return specifier.imported.type === "Identifier" ? specifier.imported.name : String(specifier.imported.value);
|
|
4367
|
+
}
|
|
4368
|
+
return specifier.local.name;
|
|
4369
|
+
};
|
|
4370
|
+
var isShadowedAt = (reference, name) => {
|
|
4371
|
+
let scope = reference.from;
|
|
4372
|
+
while (scope !== null && scope.type !== "module" && scope.type !== "global") {
|
|
4373
|
+
if (scope.set.has(name)) {
|
|
4374
|
+
return true;
|
|
4375
|
+
}
|
|
4376
|
+
scope = scope.upper;
|
|
4377
|
+
}
|
|
4378
|
+
return false;
|
|
4379
|
+
};
|
|
4380
|
+
var isRewritable = (reference, name) => {
|
|
4381
|
+
const identifier = reference.identifier;
|
|
4382
|
+
return identifier.type !== "JSXIdentifier" && identifier.parent?.type !== "ExportSpecifier" && !isShadowedAt(reference, name);
|
|
4383
|
+
};
|
|
4384
|
+
var referenceEdit = (reference, local, member) => {
|
|
4385
|
+
const identifier = reference.identifier;
|
|
4386
|
+
const parent = identifier.parent;
|
|
4387
|
+
if (parent?.type === "Property" && parent.shorthand && parent.value === identifier) {
|
|
4388
|
+
return { range: parent.range, text: `${local}: ${member}` };
|
|
4389
|
+
}
|
|
4390
|
+
return { range: identifier.range, text: member };
|
|
4391
|
+
};
|
|
4392
|
+
var rewriteBindings = (bindings, name) => {
|
|
4393
|
+
const references = bindings.flatMap(({ variable, member }) => {
|
|
4394
|
+
return variable.references.map((reference) => {
|
|
4395
|
+
return { reference, local: variable.name, member };
|
|
4396
|
+
});
|
|
4397
|
+
});
|
|
4398
|
+
const allRewritable = references.every(({ reference }) => {
|
|
4399
|
+
return isRewritable(reference, name);
|
|
4400
|
+
});
|
|
4401
|
+
if (!allRewritable) {
|
|
4402
|
+
return null;
|
|
4403
|
+
}
|
|
4404
|
+
return references.map(({ reference, local, member }) => {
|
|
4405
|
+
return referenceEdit(reference, local, member === "" ? name : `${name}.${member}`);
|
|
4406
|
+
});
|
|
4407
|
+
};
|
|
4408
|
+
var typeImportLine = (declaration, sourceCode) => {
|
|
4409
|
+
const names = declaration.specifiers.filter(isTypeOnly).map((specifier) => {
|
|
4410
|
+
return sourceCode.getText(specifier).replace(/^type\s+/, "");
|
|
4411
|
+
});
|
|
4412
|
+
return names.length === 0 ? null : `import type { ${names.join(", ")} } from ${sourceCode.getText(declaration.source)}`;
|
|
4413
|
+
};
|
|
4414
|
+
var lineRange = (declaration, sourceCode) => {
|
|
4415
|
+
const [start, end] = declaration.range;
|
|
4416
|
+
return [start, sourceCode.text[end] === "\n" ? end + 1 : end];
|
|
4417
|
+
};
|
|
4418
|
+
var isNameTaken = (sourceCode, program, name, except) => {
|
|
4419
|
+
const moduleScope = sourceCode.getScope(program).childScopes.find((scope) => {
|
|
4420
|
+
return scope.type === "module";
|
|
4421
|
+
});
|
|
4422
|
+
const variable = moduleScope?.set.get(name);
|
|
4423
|
+
return variable !== void 0 && variable.defs.some((def) => {
|
|
4424
|
+
return def.node !== except;
|
|
4425
|
+
});
|
|
4426
|
+
};
|
|
4427
|
+
var namespacesOf = (group) => {
|
|
4428
|
+
return group.flatMap((declaration) => {
|
|
4429
|
+
return declaration.specifiers.filter((specifier) => {
|
|
4430
|
+
return specifier.type === "ImportNamespaceSpecifier";
|
|
4431
|
+
});
|
|
4432
|
+
});
|
|
4433
|
+
};
|
|
4434
|
+
var reportWrongNamespaces = (ctx, wrong) => {
|
|
4435
|
+
const { context, sourceCode, source, name } = ctx;
|
|
4436
|
+
for (const specifier of wrong) {
|
|
4437
|
+
const variable = sourceCode.getDeclaredVariables(specifier)[0];
|
|
4438
|
+
const edits = ctx.canFix && wrong.length === 1 && variable ? rewriteBindings([{ variable, member: "" }], name) : null;
|
|
4439
|
+
context.report({
|
|
4440
|
+
node: specifier,
|
|
4441
|
+
messageId: "namespaceName",
|
|
4442
|
+
data: { source, name, actual: specifier.local.name },
|
|
4443
|
+
fix: edits === null ? null : (fixer) => {
|
|
4444
|
+
return [
|
|
4445
|
+
fixer.replaceText(specifier.local, name),
|
|
4446
|
+
...edits.map((edit) => {
|
|
4447
|
+
return fixer.replaceTextRange(edit.range, edit.text);
|
|
4448
|
+
})
|
|
4449
|
+
];
|
|
4450
|
+
}
|
|
4451
|
+
});
|
|
4452
|
+
}
|
|
4453
|
+
};
|
|
4454
|
+
var reportNamedImports = (ctx, offending, hasNamespace) => {
|
|
4455
|
+
const { context, sourceCode, source, name } = ctx;
|
|
4456
|
+
const bindings = offending.flatMap((declaration) => {
|
|
4457
|
+
return declaration.specifiers.filter(isValueBinding).map((specifier) => {
|
|
4458
|
+
return { variable: sourceCode.getDeclaredVariables(specifier)[0], member: importedName(specifier) };
|
|
4459
|
+
});
|
|
4460
|
+
});
|
|
4461
|
+
const edits = ctx.canFix ? rewriteBindings(bindings, name) : null;
|
|
4462
|
+
const host = hasNamespace ? void 0 : offending[0];
|
|
4463
|
+
const declarationFix = (fixer, declaration) => {
|
|
4464
|
+
const lines = [
|
|
4465
|
+
declaration === host ? `import * as ${name} from ${sourceCode.getText(declaration.source)}` : null,
|
|
4466
|
+
typeImportLine(declaration, sourceCode)
|
|
4467
|
+
].filter((line) => {
|
|
4468
|
+
return line !== null;
|
|
4469
|
+
});
|
|
4470
|
+
return lines.length === 0 ? fixer.removeRange(lineRange(declaration, sourceCode)) : fixer.replaceText(declaration, lines.join("\n"));
|
|
4471
|
+
};
|
|
4472
|
+
for (const declaration of offending) {
|
|
4473
|
+
context.report({
|
|
4474
|
+
node: declaration,
|
|
4475
|
+
messageId: "namedImport",
|
|
4476
|
+
data: { source, name },
|
|
4477
|
+
fix: edits === null || declaration !== offending[0] ? null : (fixer) => {
|
|
4478
|
+
return [
|
|
4479
|
+
...offending.map((each) => {
|
|
4480
|
+
return declarationFix(fixer, each);
|
|
4481
|
+
}),
|
|
4482
|
+
...edits.map((edit) => {
|
|
4483
|
+
return fixer.replaceTextRange(edit.range, edit.text);
|
|
4484
|
+
})
|
|
4485
|
+
];
|
|
4486
|
+
}
|
|
4487
|
+
});
|
|
4488
|
+
}
|
|
4489
|
+
};
|
|
4490
|
+
var checkGroup = (ctx, program) => {
|
|
4491
|
+
const namespaces = namespacesOf(ctx.group);
|
|
4492
|
+
const correct = namespaces.find((specifier) => {
|
|
4493
|
+
return specifier.local.name === ctx.name;
|
|
4494
|
+
});
|
|
4495
|
+
const wrong = namespaces.filter((specifier) => {
|
|
4496
|
+
return specifier !== correct;
|
|
4497
|
+
});
|
|
4498
|
+
const nameTaken = isNameTaken(ctx.sourceCode, program, ctx.name, correct);
|
|
4499
|
+
reportWrongNamespaces({ ...ctx, canFix: ctx.canFix && correct === void 0 && !nameTaken }, wrong);
|
|
4500
|
+
const offending = ctx.group.filter((declaration) => {
|
|
4501
|
+
return declaration.specifiers.some(isValueBinding);
|
|
4502
|
+
});
|
|
4503
|
+
if (offending.length > 0) {
|
|
4504
|
+
reportNamedImports({ ...ctx, canFix: ctx.canFix && wrong.length === 0 && !nameTaken }, offending, !!correct);
|
|
4505
|
+
}
|
|
4506
|
+
};
|
|
4507
|
+
var servicesNamespaceImport = {
|
|
4508
|
+
meta: {
|
|
4509
|
+
type: "suggestion",
|
|
4510
|
+
fixable: "code",
|
|
4511
|
+
docs: {
|
|
4512
|
+
description: "Import a feature's `services` module only as a namespace (`import * as service from '../services'`).",
|
|
4513
|
+
recommended: true,
|
|
4514
|
+
url: "https://github.com/ArthurSaenz/infra-kit/tree/main/apps/infra-kit/eslint-plugin#services-namespace-import"
|
|
4515
|
+
},
|
|
4516
|
+
schema: [
|
|
4517
|
+
{
|
|
4518
|
+
type: "object",
|
|
4519
|
+
properties: {
|
|
4520
|
+
name: {
|
|
4521
|
+
type: "string",
|
|
4522
|
+
minLength: 1,
|
|
4523
|
+
description: `The namespace binding (default '${DEFAULT_NAME}').`
|
|
4524
|
+
},
|
|
4525
|
+
paths: {
|
|
4526
|
+
type: "array",
|
|
4527
|
+
items: { type: "string" },
|
|
4528
|
+
description: "Optional glob patterns. When provided, the rule only runs for files whose path matches one of them."
|
|
4529
|
+
},
|
|
4530
|
+
ignore: {
|
|
4531
|
+
type: "array",
|
|
4532
|
+
items: { type: "string" },
|
|
4533
|
+
description: "Optional glob patterns. The rule is skipped for files whose path matches one of them, even if it also matches `paths`."
|
|
4534
|
+
}
|
|
4535
|
+
},
|
|
4536
|
+
additionalProperties: false
|
|
4537
|
+
}
|
|
4538
|
+
],
|
|
4539
|
+
messages: {
|
|
4540
|
+
namedImport: "Import `{{source}}` as a namespace \u2014 `import * as {{name}} from '{{source}}'` \u2014 and read members as `{{name}}.<member>`.",
|
|
4541
|
+
namespaceName: "Name the `{{source}}` namespace `{{name}}`, not `{{actual}}`."
|
|
4542
|
+
}
|
|
4543
|
+
},
|
|
4544
|
+
create(context) {
|
|
4545
|
+
const options = context.options[0] ?? {};
|
|
4546
|
+
const filename = context.filename.split("\\").join("/");
|
|
4547
|
+
if (BARREL_FILE.test(filename) || !isInScope(filename, options.paths ?? [], options.ignore ?? []) || !isUiPackageFile(filename)) {
|
|
4548
|
+
return {};
|
|
4549
|
+
}
|
|
4550
|
+
const declarations = [];
|
|
4551
|
+
return {
|
|
4552
|
+
ImportDeclaration(node) {
|
|
4553
|
+
if (!isTypeOnly(node) && typeof node.source.value === "string" && SERVICES_SPECIFIER.test(node.source.value)) {
|
|
4554
|
+
declarations.push(node);
|
|
4555
|
+
}
|
|
4556
|
+
},
|
|
4557
|
+
"Program:exit": function(program) {
|
|
4558
|
+
const bySource = Map.groupBy(declarations, (declaration) => {
|
|
4559
|
+
return String(declaration.source.value);
|
|
4560
|
+
});
|
|
4561
|
+
for (const [source, group] of bySource) {
|
|
4562
|
+
checkGroup(
|
|
4563
|
+
{
|
|
4564
|
+
context,
|
|
4565
|
+
sourceCode: context.sourceCode,
|
|
4566
|
+
source,
|
|
4567
|
+
group,
|
|
4568
|
+
name: options.name ?? DEFAULT_NAME,
|
|
4569
|
+
// Two different services modules cannot share one namespace name.
|
|
4570
|
+
canFix: bySource.size === 1
|
|
4571
|
+
},
|
|
4572
|
+
program
|
|
4573
|
+
);
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
};
|
|
4577
|
+
}
|
|
4578
|
+
};
|
|
4579
|
+
|
|
4351
4580
|
// src/rules/test-location/test-location.ts
|
|
4352
4581
|
var TEST_FILE3 = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
4353
4582
|
var SPEC_SUFFIX = /\.spec(\.[cm]?[jt]sx?)$/;
|
|
@@ -4470,6 +4699,7 @@ var rules = {
|
|
|
4470
4699
|
"atom-naming": atomNaming,
|
|
4471
4700
|
"no-hooks-in-services": noHooksInServices,
|
|
4472
4701
|
"feature-files": featureFiles,
|
|
4702
|
+
"services-namespace-import": servicesNamespaceImport,
|
|
4473
4703
|
"component-name-matches-file": componentNameMatchesFile,
|
|
4474
4704
|
"component-classname": componentClassname,
|
|
4475
4705
|
"dumb-component-purity": dumbComponentPurity
|
|
@@ -4480,7 +4710,7 @@ var PLUGIN_NAME = "@wl";
|
|
|
4480
4710
|
var plugin = {
|
|
4481
4711
|
meta: {
|
|
4482
4712
|
name: "@wl/eslint-plugin",
|
|
4483
|
-
version: "0.
|
|
4713
|
+
version: "0.45.0"
|
|
4484
4714
|
},
|
|
4485
4715
|
rules,
|
|
4486
4716
|
configs: {}
|
|
@@ -4605,6 +4835,7 @@ plugin.configs.recommended = [
|
|
|
4605
4835
|
[`${PLUGIN_NAME}/atom-naming`]: "error",
|
|
4606
4836
|
[`${PLUGIN_NAME}/no-hooks-in-services`]: "error",
|
|
4607
4837
|
[`${PLUGIN_NAME}/feature-files`]: "error",
|
|
4838
|
+
[`${PLUGIN_NAME}/services-namespace-import`]: "error",
|
|
4608
4839
|
[`${PLUGIN_NAME}/component-name-matches-file`]: "error",
|
|
4609
4840
|
[`${PLUGIN_NAME}/component-classname`]: "error",
|
|
4610
4841
|
[`${PLUGIN_NAME}/dumb-component-purity`]: "error"
|