@drupal-canvas/eslint-config 0.2.1 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +6 -5
  2. package/dist/index.js +116 -16
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -33,11 +33,12 @@ export default defineConfig([
33
33
  The following custom rules are part of the `required` config and validate Drupal
34
34
  Canvas Code Components:
35
35
 
36
- | Rule | Description |
37
- | ---------------------- | ------------------------------------------------------------------------------------------------------- |
38
- | `component-dir-name` | Validates that `machineName` matches the directory name (index-style) or filename prefix (named-style). |
39
- | `component-exports` | Validates that component has a default export. |
40
- | `component-prop-names` | Validates that component prop IDs match the camelCase version of their titles. |
36
+ | Rule | Description |
37
+ | ------------------------------ | ------------------------------------------------------------------------------------------------------- |
38
+ | `component-dir-name` | Validates that `machineName` matches the directory name (index-style) or filename prefix (named-style). |
39
+ | `component-exports` | Validates that component has a default export. |
40
+ | `component-image-example-urls` | Validates that `canvas.module/image` prop examples use fully qualified image URLs. |
41
+ | `component-prop-names` | Validates that component prop IDs match the camelCase version of their titles. |
41
42
 
42
43
  ### Deprecated rules
43
44
 
package/dist/index.js CHANGED
@@ -320,9 +320,9 @@ var require_ignore = __commonJS({
320
320
  };
321
321
  }
322
322
  if (checkPattern(pattern.pattern)) {
323
- const rule8 = createRule(pattern, this._ignoreCase);
323
+ const rule9 = createRule(pattern, this._ignoreCase);
324
324
  this._added = true;
325
- this._rules.push(rule8);
325
+ this._rules.push(rule9);
326
326
  }
327
327
  }
328
328
  // @param {Array<string> | string | Ignore} pattern
@@ -344,18 +344,18 @@ var require_ignore = __commonJS({
344
344
  let ignored = false;
345
345
  let unignored = false;
346
346
  let matchedRule;
347
- this._rules.forEach((rule8) => {
348
- const { negative } = rule8;
347
+ this._rules.forEach((rule9) => {
348
+ const { negative } = rule9;
349
349
  if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
350
350
  return;
351
351
  }
352
- const matched = rule8[mode].test(path2);
352
+ const matched = rule9[mode].test(path2);
353
353
  if (!matched) {
354
354
  return;
355
355
  }
356
356
  ignored = !negative;
357
357
  unignored = negative;
358
- matchedRule = negative ? UNDEFINED : rule8;
358
+ matchedRule = negative ? UNDEFINED : rule9;
359
359
  });
360
360
  const ret = {
361
361
  ignored,
@@ -668,6 +668,104 @@ var rule2 = {
668
668
  }
669
669
  };
670
670
  var component_exports_default = rule2;
671
+
672
+ // src/rules/component-image-example-urls.ts
673
+ var IMAGE_REF = "json-schema-definitions://canvas.module/image";
674
+ function getMappingPair(mapping, key) {
675
+ return mapping.pairs.find((pair) => getYAMLStringValue(pair.key) === key);
676
+ }
677
+ function isYamlMapping(node) {
678
+ return node?.type === "YAMLMapping";
679
+ }
680
+ function isYamlSequence(node) {
681
+ return node?.type === "YAMLSequence";
682
+ }
683
+ function hasImageRef(propMapping) {
684
+ const ref = getYAMLStringValue(
685
+ getMappingPair(propMapping, "$ref")?.value ?? null
686
+ );
687
+ if (ref === IMAGE_REF) {
688
+ return true;
689
+ }
690
+ const itemsValue = getMappingPair(propMapping, "items")?.value;
691
+ if (!isYamlMapping(itemsValue)) {
692
+ return false;
693
+ }
694
+ return getYAMLStringValue(getMappingPair(itemsValue, "$ref")?.value ?? null) === IMAGE_REF;
695
+ }
696
+ function isFullyQualifiedUrl(value) {
697
+ try {
698
+ const parsed = new URL(value);
699
+ return parsed.protocol.length > 0 && parsed.hostname.length > 0;
700
+ } catch {
701
+ return false;
702
+ }
703
+ }
704
+ function getInvalidImageExampleNodes(examplesValue) {
705
+ if (!isYamlSequence(examplesValue)) {
706
+ return [];
707
+ }
708
+ const invalidNodes = [];
709
+ for (const example of examplesValue.entries) {
710
+ const imageEntries = isYamlSequence(example) ? example.entries : [example];
711
+ for (const imageEntry of imageEntries) {
712
+ if (!isYamlMapping(imageEntry)) {
713
+ continue;
714
+ }
715
+ const srcNode = getMappingPair(imageEntry, "src")?.value;
716
+ if (srcNode?.type === "YAMLScalar" && typeof srcNode.value === "string" && !isFullyQualifiedUrl(srcNode.value)) {
717
+ invalidNodes.push(srcNode);
718
+ }
719
+ }
720
+ }
721
+ return invalidNodes;
722
+ }
723
+ var rule3 = {
724
+ meta: {
725
+ type: "problem",
726
+ docs: {
727
+ description: "Validates that default examples for image props use fully-qualified URLs"
728
+ }
729
+ },
730
+ create(context) {
731
+ if (!isComponentYmlFile(context.filename)) {
732
+ return {};
733
+ }
734
+ return {
735
+ YAMLPair(node) {
736
+ const keyName = getYAMLStringValue(node.key);
737
+ if (keyName !== "props" || !isYamlMapping(node.value)) {
738
+ return;
739
+ }
740
+ const propertiesValue = getMappingPair(node.value, "properties")?.value;
741
+ if (!isYamlMapping(propertiesValue)) {
742
+ return;
743
+ }
744
+ for (const propPair of propertiesValue.pairs) {
745
+ const propId = getYAMLStringValue(propPair.key);
746
+ if (!propId || !isYamlMapping(propPair.value)) {
747
+ continue;
748
+ }
749
+ if (!hasImageRef(propPair.value)) {
750
+ continue;
751
+ }
752
+ const examplesValue = getMappingPair(
753
+ propPair.value,
754
+ "examples"
755
+ )?.value;
756
+ const invalidNodes = getInvalidImageExampleNodes(examplesValue);
757
+ for (const invalidNode of invalidNodes) {
758
+ context.report({
759
+ node: invalidNode,
760
+ message: `Image prop "${propId}" example src must be a fully-qualified URL with both scheme and host. Use a placeholder URL such as https://placehold.co/600x400.`
761
+ });
762
+ }
763
+ }
764
+ }
765
+ };
766
+ }
767
+ };
768
+ var component_image_example_urls_default = rule3;
671
769
  var Gt = (n7, t, e) => {
672
770
  let s = n7 instanceof RegExp ? ce(n7, e) : n7, i = t instanceof RegExp ? ce(t, e) : t, r = s !== null && i != null && ss(s, i, e);
673
771
  return r && { start: r[0], end: r[1], pre: e.slice(0, r[0]), body: e.slice(r[0] + s.length, r[1]), post: e.slice(r[1] + i.length) };
@@ -4572,7 +4670,7 @@ function checkImportSource(context, node, source) {
4572
4670
  return;
4573
4671
  }
4574
4672
  }
4575
- var rule3 = {
4673
+ var rule4 = {
4576
4674
  meta: {
4577
4675
  type: "problem",
4578
4676
  docs: {
@@ -4598,7 +4696,7 @@ var rule3 = {
4598
4696
  };
4599
4697
  }
4600
4698
  };
4601
- var component_imports_default = rule3;
4699
+ var component_imports_default = rule4;
4602
4700
  function extractProps(propsNode) {
4603
4701
  if (!propsNode.value || propsNode.value.type !== "YAMLMapping") {
4604
4702
  return [];
@@ -4632,7 +4730,7 @@ function extractProps(propsNode) {
4632
4730
  }
4633
4731
  return props;
4634
4732
  }
4635
- var rule4 = {
4733
+ var rule5 = {
4636
4734
  meta: {
4637
4735
  type: "problem",
4638
4736
  docs: {
@@ -4673,7 +4771,7 @@ var rule4 = {
4673
4771
  };
4674
4772
  }
4675
4773
  };
4676
- var component_prop_names_default = rule4;
4774
+ var component_prop_names_default = rule5;
4677
4775
 
4678
4776
  // src/configs/required.ts
4679
4777
  var required = defineConfig([
@@ -4699,6 +4797,7 @@ var required = defineConfig([
4699
4797
  rules: {
4700
4798
  "component-dir-name": component_dir_name_default,
4701
4799
  "component-exports": component_exports_default,
4800
+ "component-image-example-urls": component_image_example_urls_default,
4702
4801
  "component-imports": component_imports_default,
4703
4802
  "component-prop-names": component_prop_names_default
4704
4803
  }
@@ -4707,6 +4806,7 @@ var required = defineConfig([
4707
4806
  rules: {
4708
4807
  "drupal-canvas/component-dir-name": "error",
4709
4808
  "drupal-canvas/component-exports": "error",
4809
+ "drupal-canvas/component-image-example-urls": "error",
4710
4810
  "drupal-canvas/component-imports": "error",
4711
4811
  "drupal-canvas/component-prop-names": "error"
4712
4812
  }
@@ -4754,7 +4854,7 @@ var IGNORED_FILES = [
4754
4854
  function isFileAllowed(fileName, allowedFiles) {
4755
4855
  return allowedFiles.some((allowedFile) => allowedFile === fileName);
4756
4856
  }
4757
- var rule5 = {
4857
+ var rule6 = {
4758
4858
  meta: {
4759
4859
  type: "problem",
4760
4860
  docs: {
@@ -4793,7 +4893,7 @@ var rule5 = {
4793
4893
  };
4794
4894
  }
4795
4895
  };
4796
- var component_files_default = rule5;
4896
+ var component_files_default = rule6;
4797
4897
  function checkImportSource2(context, node, source) {
4798
4898
  if (source.startsWith("./") || source.startsWith("../")) {
4799
4899
  context.report({
@@ -4922,7 +5022,7 @@ function checkImportSource2(context, node, source) {
4922
5022
  message: `Importing "${source}" is not supported. If this is a local import via a path alias, use the "@/components/" alias instead. If you are importing a third-party package, see the list of supported packages at https://project.pages.drupalcode.org/canvas/code-components/packages. (The status of supporting any third-party package can be tracked at https://drupal.org/i/3560197.)`
4923
5023
  });
4924
5024
  }
4925
- var rule6 = {
5025
+ var rule7 = {
4926
5026
  meta: {
4927
5027
  type: "problem",
4928
5028
  docs: {
@@ -4949,7 +5049,7 @@ var rule6 = {
4949
5049
  };
4950
5050
  }
4951
5051
  };
4952
- var component_imports_deprecated_default = rule6;
5052
+ var component_imports_deprecated_default = rule7;
4953
5053
  function findTopmostComponentsParentDir(currentParentDir, rootDir) {
4954
5054
  if (currentParentDir === rootDir) {
4955
5055
  return currentParentDir;
@@ -4970,7 +5070,7 @@ function hasComponentSubdirectories(dirPath) {
4970
5070
  }
4971
5071
  return false;
4972
5072
  }
4973
- var rule7 = {
5073
+ var rule8 = {
4974
5074
  meta: {
4975
5075
  type: "problem",
4976
5076
  docs: {
@@ -5005,7 +5105,7 @@ var rule7 = {
5005
5105
  };
5006
5106
  }
5007
5107
  };
5008
- var component_no_hierarchy_default = rule7;
5108
+ var component_no_hierarchy_default = rule8;
5009
5109
 
5010
5110
  // src/configs/requiredDeprecated.ts
5011
5111
  var required2 = defineConfig([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drupal-canvas/eslint-config",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "ESLint config for validating Drupal Canvas Code Components",
5
5
  "license": "MIT",
6
6
  "repository": {