repopo 0.7.1 → 0.8.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 (102) hide show
  1. package/CHANGELOG.md +171 -6
  2. package/THIRD-PARTY-LICENSES.txt +149 -3
  3. package/esm/adapters/fluidFramework.d.ts +103 -0
  4. package/esm/adapters/fluidFramework.d.ts.map +1 -0
  5. package/esm/adapters/fluidFramework.js +119 -0
  6. package/esm/adapters/fluidFramework.js.map +1 -0
  7. package/esm/baseCommand.d.ts +1 -0
  8. package/esm/baseCommand.d.ts.map +1 -1
  9. package/esm/baseCommand.js +2 -1
  10. package/esm/baseCommand.js.map +1 -1
  11. package/esm/commands/check.d.ts.map +1 -1
  12. package/esm/commands/check.js +9 -8
  13. package/esm/commands/check.js.map +1 -1
  14. package/esm/index.d.ts +1 -0
  15. package/esm/index.d.ts.map +1 -1
  16. package/esm/index.js +1 -0
  17. package/esm/index.js.map +1 -1
  18. package/esm/perf.d.ts +4 -1
  19. package/esm/perf.d.ts.map +1 -1
  20. package/esm/perf.js +3 -2
  21. package/esm/perf.js.map +1 -1
  22. package/esm/policies/LicenseFileExists.d.ts.map +1 -1
  23. package/esm/policies/LicenseFileExists.js +3 -1
  24. package/esm/policies/LicenseFileExists.js.map +1 -1
  25. package/esm/policies/NoJsFileExtensions.d.ts.map +1 -1
  26. package/esm/policies/NoJsFileExtensions.js +4 -1
  27. package/esm/policies/NoJsFileExtensions.js.map +1 -1
  28. package/esm/policies/NoLargeBinaryFiles.d.ts.map +1 -1
  29. package/esm/policies/NoLargeBinaryFiles.js +3 -1
  30. package/esm/policies/NoLargeBinaryFiles.js.map +1 -1
  31. package/esm/policies/NoPrivateWorkspaceDependencies.d.ts +37 -0
  32. package/esm/policies/NoPrivateWorkspaceDependencies.d.ts.map +1 -0
  33. package/esm/policies/NoPrivateWorkspaceDependencies.js +137 -0
  34. package/esm/policies/NoPrivateWorkspaceDependencies.js.map +1 -0
  35. package/esm/policies/PackageAllowedScopes.d.ts +88 -0
  36. package/esm/policies/PackageAllowedScopes.d.ts.map +1 -0
  37. package/esm/policies/PackageAllowedScopes.js +113 -0
  38. package/esm/policies/PackageAllowedScopes.js.map +1 -0
  39. package/esm/policies/PackageEsmType.d.ts +100 -0
  40. package/esm/policies/PackageEsmType.d.ts.map +1 -0
  41. package/esm/policies/PackageEsmType.js +257 -0
  42. package/esm/policies/PackageEsmType.js.map +1 -0
  43. package/esm/policies/PackageFolderName.d.ts +97 -0
  44. package/esm/policies/PackageFolderName.d.ts.map +1 -0
  45. package/esm/policies/PackageFolderName.js +104 -0
  46. package/esm/policies/PackageFolderName.js.map +1 -0
  47. package/esm/policies/PackageJsonProperties.d.ts.map +1 -1
  48. package/esm/policies/PackageJsonProperties.js +2 -1
  49. package/esm/policies/PackageJsonProperties.js.map +1 -1
  50. package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts.map +1 -1
  51. package/esm/policies/PackageJsonRepoDirectoryProperty.js +4 -3
  52. package/esm/policies/PackageJsonRepoDirectoryProperty.js.map +1 -1
  53. package/esm/policies/PackageJsonSorted.d.ts.map +1 -1
  54. package/esm/policies/PackageJsonSorted.js +11 -3
  55. package/esm/policies/PackageJsonSorted.js.map +1 -1
  56. package/esm/policies/PackageLicense.d.ts +61 -0
  57. package/esm/policies/PackageLicense.d.ts.map +1 -0
  58. package/esm/policies/PackageLicense.js +131 -0
  59. package/esm/policies/PackageLicense.js.map +1 -0
  60. package/esm/policies/PackagePrivateField.d.ts +124 -0
  61. package/esm/policies/PackagePrivateField.d.ts.map +1 -0
  62. package/esm/policies/PackagePrivateField.js +155 -0
  63. package/esm/policies/PackagePrivateField.js.map +1 -0
  64. package/esm/policies/PackageReadme.d.ts +77 -0
  65. package/esm/policies/PackageReadme.d.ts.map +1 -0
  66. package/esm/policies/PackageReadme.js +209 -0
  67. package/esm/policies/PackageReadme.js.map +1 -0
  68. package/esm/policies/PackageScripts.d.ts +180 -2
  69. package/esm/policies/PackageScripts.d.ts.map +1 -1
  70. package/esm/policies/PackageScripts.js +349 -29
  71. package/esm/policies/PackageScripts.js.map +1 -1
  72. package/esm/policies/PackageTestScripts.d.ts +100 -0
  73. package/esm/policies/PackageTestScripts.d.ts.map +1 -0
  74. package/esm/policies/PackageTestScripts.js +149 -0
  75. package/esm/policies/PackageTestScripts.js.map +1 -0
  76. package/esm/policies/RequiredGitignorePatterns.d.ts.map +1 -1
  77. package/esm/policies/RequiredGitignorePatterns.js +10 -4
  78. package/esm/policies/RequiredGitignorePatterns.js.map +1 -1
  79. package/esm/policies.d.ts +9 -1
  80. package/esm/policies.d.ts.map +1 -1
  81. package/esm/policies.js +10 -1
  82. package/esm/policies.js.map +1 -1
  83. package/esm/policy.d.ts +5 -1
  84. package/esm/policy.d.ts.map +1 -1
  85. package/esm/policy.js.map +1 -1
  86. package/esm/policyDefiners/defineFileHeaderPolicy.d.ts.map +1 -1
  87. package/esm/policyDefiners/defineFileHeaderPolicy.js +2 -1
  88. package/esm/policyDefiners/defineFileHeaderPolicy.js.map +1 -1
  89. package/esm/policyDefiners/definePackagePolicy.d.ts.map +1 -1
  90. package/esm/policyDefiners/definePackagePolicy.js +2 -1
  91. package/esm/policyDefiners/definePackagePolicy.js.map +1 -1
  92. package/esm/tsdoc-metadata.json +1 -1
  93. package/esm/utils/indentation.d.ts +73 -0
  94. package/esm/utils/indentation.d.ts.map +1 -0
  95. package/esm/utils/indentation.js +97 -0
  96. package/esm/utils/indentation.js.map +1 -0
  97. package/esm/utils/packageName.d.ts +71 -0
  98. package/esm/utils/packageName.d.ts.map +1 -0
  99. package/esm/utils/packageName.js +79 -0
  100. package/esm/utils/packageName.js.map +1 -0
  101. package/oclif.manifest.json +1 -1
  102. package/package.json +30 -15
@@ -1 +1 @@
1
- {"version":3,"file":"defineFileHeaderPolicy.d.ts","sourceRoot":"","sources":["../../src/policyDefiners/defineFileHeaderPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,gBAAgB,EAGhB,MAAM,cAAc,CAAC;AAItB;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACtC;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,yBAChB,SAAQ,OAAO,CAAC,sBAAsB,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,KAAK,MAAM,CAAC;CACtE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACrC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,yBAAyB,GAC/B,gBAAgB,CAAC,sBAAsB,CAAC,CAiE1C"}
1
+ {"version":3,"file":"defineFileHeaderPolicy.d.ts","sourceRoot":"","sources":["../../src/policyDefiners/defineFileHeaderPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,gBAAgB,EAGhB,MAAM,cAAc,CAAC;AAItB;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACtC;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,yBAChB,SAAQ,OAAO,CAAC,sBAAsB,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,KAAK,MAAM,CAAC;CACtE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACrC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,yBAAyB,GAC/B,gBAAgB,CAAC,sBAAsB,CAAC,CAkE1C"}
@@ -34,13 +34,14 @@ export function defineFileHeaderPolicy(name, config) {
34
34
  name,
35
35
  file,
36
36
  autoFixable: true,
37
+ errorMessages: [],
37
38
  };
38
39
  // TODO: Consider reading only the first 512B or so since headers are typically
39
40
  // at the beginning of the file.
40
41
  const content = await readFile(file, { encoding: "utf8" });
41
42
  const failed = !regex.test(content);
42
43
  if (failed) {
43
- failResult.errorMessage = `${extname(file)} file missing header`;
44
+ failResult.errorMessages.push(`${extname(file)} file missing header`);
44
45
  }
45
46
  if (failed) {
46
47
  if (resolve) {
@@ -1 +1 @@
1
- {"version":3,"file":"defineFileHeaderPolicy.js","sourceRoot":"","sources":["../../src/policyDefiners/defineFileHeaderPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,GAAG,IAAI,OAAO,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAOhC,MAAM,cAAc,GAAG,aAAa,CAAC;AA+CrC;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACrC,IAAY,EACZ,MAAiC;IAEjC,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,EAAE,MAAM,IAAI,EAAE,CAAC;IAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;IACtC,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;IAClC,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,IAAI,EAAE,CAAC;IAE5C,2EAA2E;IAC3E,mGAAmG;IACnG,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE,CACxC,IAAI;SACF,KAAK,CAAC,OAAO,CAAC;SACd,GAAG,CACH,CAAC,IAAI,EAAE,EAAE,CACR,GAAG,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,GAAG,GAAG,EAAE,CAC/D;SACA,IAAI,CAAC,EAAE,CAAC;SACR,OAAO,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC,CAAC,uCAAuC;IAE9E,wFAAwF;IACxF,0FAA0F;IAC1F,MAAM,KAAK,GAAG,IAAI,MAAM,CACvB,IAAI,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,CAC1F,CAAC;IAEF,OAAO;QACN,IAAI;QACJ,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,EAAE;YAC1D,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;gBAChC,OAAO,IAAI,CAAC;YACb,CAAC;YAED,MAAM,UAAU,GAAkB;gBACjC,IAAI;gBACJ,IAAI;gBACJ,WAAW,EAAE,IAAI;aACjB,CAAC;YAEF,+EAA+E;YAC/E,gCAAgC;YAChC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;YAC3D,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAEpC,IAAI,MAAM,EAAE,CAAC;gBACZ,UAAU,CAAC,YAAY,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC;YAClE,CAAC;YAED,IAAI,MAAM,EAAE,CAAC;gBACZ,IAAI,OAAO,EAAE,CAAC;oBACb,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;oBAC1D,MAAM,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;oBAElC,MAAM,SAAS,GAAoB;wBAClC,GAAG,UAAU;wBACb,QAAQ,EAAE,IAAI;qBACd,CAAC;oBAEF,OAAO,SAAS,CAAC;gBAClB,CAAC;gBACD,OAAO,UAAU,CAAC;YACnB,CAAC;YAED,OAAO,IAAI,CAAC;QACb,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport { EOL as newline } from \"node:os\";\nimport { extname } from \"pathe\";\nimport type {\n\tPolicyDefinition,\n\tPolicyFailure,\n\tPolicyFixResult,\n} from \"../policy.js\";\n\nconst trailingSpaces = /\\s*\\\\r\\?\\\\n/;\n\n/**\n * @alpha\n */\nexport interface FileHeaderPolicyConfig {\n\t/**\n\t * The text to use as the header.\n\t */\n\theaderText: string;\n\n\t/**\n\t * An optional string that will be appended to the headerText.\n\t */\n\tautoGenText?: string;\n}\n\n/**\n * @alpha\n */\nexport interface FileHeaderGeneratorConfig\n\textends Partial<FileHeaderPolicyConfig> {\n\tmatch: RegExp;\n\n\t/**\n\t * Regex matching header prefix (e.g. `/*!\\r?\\n`)\n\t */\n\theaderStart?: RegExp;\n\n\t/**\n\t * Regex matching beginning of each line (e.g. ' * ')\n\t */\n\tlineStart: RegExp;\n\n\t/**\n\t * Regex matching the end of each line (e.g., `\\r?\\n`)\n\t */\n\tlineEnd: RegExp;\n\n\t/**\n\t * Regex matching the header postfix.\n\t */\n\theaderEnd?: RegExp;\n\n\treplacer: (content: string, config: FileHeaderPolicyConfig) => string;\n}\n\n/**\n * Given a {@link FileHeaderPolicyConfig}, produces a function that detects correct file headers\n * and returns an error string if the header is missing or incorrect.\n *\n * @alpha\n */\nexport function defineFileHeaderPolicy(\n\tname: string,\n\tconfig: FileHeaderGeneratorConfig,\n): PolicyDefinition<FileHeaderPolicyConfig> {\n\tconst pre = config.headerStart?.source ?? \"\";\n\tconst start = config.lineStart.source;\n\tconst end = config.lineEnd.source;\n\tconst post = config.headerEnd?.source ?? \"\";\n\n\t// Helper which constructs a matching RegExp from a given multiline string.\n\t// (See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping)\n\tconst toRegex = (text: string): string =>\n\t\ttext\n\t\t\t.split(newline)\n\t\t\t.map(\n\t\t\t\t(line) =>\n\t\t\t\t\t`${start}${line.replace(/[$()*+.?[\\\\\\]^{|}]/g, \"\\\\$&\")}${end}`,\n\t\t\t)\n\t\t\t.join(\"\")\n\t\t\t.replace(trailingSpaces, \"\\\\r?\\\\n\"); // Trim trailing spaces at end-of-line.\n\n\t// Detection regex matches the header start (if any), followed by lines in 'headerText',\n\t// optionally followed by lines in the 'autoGenText', and finally the header end (if any).\n\tconst regex = new RegExp(\n\t\t`^${pre}${toRegex(config.headerText ?? \"\")}(${toRegex(config.autoGenText ?? \"\")})?${post}`,\n\t);\n\n\treturn {\n\t\tname,\n\t\tmatch: config.match,\n\t\thandler: async ({ file, resolve, config: policyConfig }) => {\n\t\t\tif (policyConfig === undefined) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tconst failResult: PolicyFailure = {\n\t\t\t\tname,\n\t\t\t\tfile,\n\t\t\t\tautoFixable: true,\n\t\t\t};\n\n\t\t\t// TODO: Consider reading only the first 512B or so since headers are typically\n\t\t\t// at the beginning of the file.\n\t\t\tconst content = await readFile(file, { encoding: \"utf8\" });\n\t\t\tconst failed = !regex.test(content);\n\n\t\t\tif (failed) {\n\t\t\t\tfailResult.errorMessage = `${extname(file)} file missing header`;\n\t\t\t}\n\n\t\t\tif (failed) {\n\t\t\t\tif (resolve) {\n\t\t\t\t\tconst newContent = config.replacer(content, policyConfig);\n\t\t\t\t\tawait writeFile(file, newContent);\n\n\t\t\t\t\tconst fixResult: PolicyFixResult = {\n\t\t\t\t\t\t...failResult,\n\t\t\t\t\t\tresolved: true,\n\t\t\t\t\t};\n\n\t\t\t\t\treturn fixResult;\n\t\t\t\t}\n\t\t\t\treturn failResult;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"defineFileHeaderPolicy.js","sourceRoot":"","sources":["../../src/policyDefiners/defineFileHeaderPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,GAAG,IAAI,OAAO,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAOhC,MAAM,cAAc,GAAG,aAAa,CAAC;AA+CrC;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACrC,IAAY,EACZ,MAAiC;IAEjC,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,EAAE,MAAM,IAAI,EAAE,CAAC;IAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;IACtC,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;IAClC,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,IAAI,EAAE,CAAC;IAE5C,2EAA2E;IAC3E,mGAAmG;IACnG,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE,CACxC,IAAI;SACF,KAAK,CAAC,OAAO,CAAC;SACd,GAAG,CACH,CAAC,IAAI,EAAE,EAAE,CACR,GAAG,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,GAAG,GAAG,EAAE,CAC/D;SACA,IAAI,CAAC,EAAE,CAAC;SACR,OAAO,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC,CAAC,uCAAuC;IAE9E,wFAAwF;IACxF,0FAA0F;IAC1F,MAAM,KAAK,GAAG,IAAI,MAAM,CACvB,IAAI,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,CAC1F,CAAC;IAEF,OAAO;QACN,IAAI;QACJ,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,EAAE;YAC1D,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;gBAChC,OAAO,IAAI,CAAC;YACb,CAAC;YAED,MAAM,UAAU,GAAkB;gBACjC,IAAI;gBACJ,IAAI;gBACJ,WAAW,EAAE,IAAI;gBACjB,aAAa,EAAE,EAAE;aACjB,CAAC;YAEF,+EAA+E;YAC/E,gCAAgC;YAChC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;YAC3D,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAEpC,IAAI,MAAM,EAAE,CAAC;gBACZ,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;YACvE,CAAC;YAED,IAAI,MAAM,EAAE,CAAC;gBACZ,IAAI,OAAO,EAAE,CAAC;oBACb,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;oBAC1D,MAAM,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;oBAElC,MAAM,SAAS,GAAoB;wBAClC,GAAG,UAAU;wBACb,QAAQ,EAAE,IAAI;qBACd,CAAC;oBAEF,OAAO,SAAS,CAAC;gBAClB,CAAC;gBACD,OAAO,UAAU,CAAC;YACnB,CAAC;YAED,OAAO,IAAI,CAAC;QACb,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport { EOL as newline } from \"node:os\";\nimport { extname } from \"pathe\";\nimport type {\n\tPolicyDefinition,\n\tPolicyFailure,\n\tPolicyFixResult,\n} from \"../policy.js\";\n\nconst trailingSpaces = /\\s*\\\\r\\?\\\\n/;\n\n/**\n * @alpha\n */\nexport interface FileHeaderPolicyConfig {\n\t/**\n\t * The text to use as the header.\n\t */\n\theaderText: string;\n\n\t/**\n\t * An optional string that will be appended to the headerText.\n\t */\n\tautoGenText?: string;\n}\n\n/**\n * @alpha\n */\nexport interface FileHeaderGeneratorConfig\n\textends Partial<FileHeaderPolicyConfig> {\n\tmatch: RegExp;\n\n\t/**\n\t * Regex matching header prefix (e.g. `/*!\\r?\\n`)\n\t */\n\theaderStart?: RegExp;\n\n\t/**\n\t * Regex matching beginning of each line (e.g. ' * ')\n\t */\n\tlineStart: RegExp;\n\n\t/**\n\t * Regex matching the end of each line (e.g., `\\r?\\n`)\n\t */\n\tlineEnd: RegExp;\n\n\t/**\n\t * Regex matching the header postfix.\n\t */\n\theaderEnd?: RegExp;\n\n\treplacer: (content: string, config: FileHeaderPolicyConfig) => string;\n}\n\n/**\n * Given a {@link FileHeaderPolicyConfig}, produces a function that detects correct file headers\n * and returns an error string if the header is missing or incorrect.\n *\n * @alpha\n */\nexport function defineFileHeaderPolicy(\n\tname: string,\n\tconfig: FileHeaderGeneratorConfig,\n): PolicyDefinition<FileHeaderPolicyConfig> {\n\tconst pre = config.headerStart?.source ?? \"\";\n\tconst start = config.lineStart.source;\n\tconst end = config.lineEnd.source;\n\tconst post = config.headerEnd?.source ?? \"\";\n\n\t// Helper which constructs a matching RegExp from a given multiline string.\n\t// (See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping)\n\tconst toRegex = (text: string): string =>\n\t\ttext\n\t\t\t.split(newline)\n\t\t\t.map(\n\t\t\t\t(line) =>\n\t\t\t\t\t`${start}${line.replace(/[$()*+.?[\\\\\\]^{|}]/g, \"\\\\$&\")}${end}`,\n\t\t\t)\n\t\t\t.join(\"\")\n\t\t\t.replace(trailingSpaces, \"\\\\r?\\\\n\"); // Trim trailing spaces at end-of-line.\n\n\t// Detection regex matches the header start (if any), followed by lines in 'headerText',\n\t// optionally followed by lines in the 'autoGenText', and finally the header end (if any).\n\tconst regex = new RegExp(\n\t\t`^${pre}${toRegex(config.headerText ?? \"\")}(${toRegex(config.autoGenText ?? \"\")})?${post}`,\n\t);\n\n\treturn {\n\t\tname,\n\t\tmatch: config.match,\n\t\thandler: async ({ file, resolve, config: policyConfig }) => {\n\t\t\tif (policyConfig === undefined) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tconst failResult: PolicyFailure = {\n\t\t\t\tname,\n\t\t\t\tfile,\n\t\t\t\tautoFixable: true,\n\t\t\t\terrorMessages: [],\n\t\t\t};\n\n\t\t\t// TODO: Consider reading only the first 512B or so since headers are typically\n\t\t\t// at the beginning of the file.\n\t\t\tconst content = await readFile(file, { encoding: \"utf8\" });\n\t\t\tconst failed = !regex.test(content);\n\n\t\t\tif (failed) {\n\t\t\t\tfailResult.errorMessages.push(`${extname(file)} file missing header`);\n\t\t\t}\n\n\t\t\tif (failed) {\n\t\t\t\tif (resolve) {\n\t\t\t\t\tconst newContent = config.replacer(content, policyConfig);\n\t\t\t\t\tawait writeFile(file, newContent);\n\n\t\t\t\t\tconst fixResult: PolicyFixResult = {\n\t\t\t\t\t\t...failResult,\n\t\t\t\t\t\tresolved: true,\n\t\t\t\t\t};\n\n\t\t\t\t\treturn fixResult;\n\t\t\t\t}\n\t\t\t\treturn failResult;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t},\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"definePackagePolicy.d.ts","sourceRoot":"","sources":["../../src/policyDefiners/definePackagePolicy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,KAAK,EACX,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,EACnB,MAAM,cAAc,CAAC;AAItB;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,EAAE,CAAC,IAAI,CACtC,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,uBAAuB,CAAC,CAAC,CAAC,KAC5B,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAElC;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,GAAG,WAAW,EAAE,CAAC,GAAG,SAAS,EACjE,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,GAErC,gBAAgB,CAAC,CAAC,CAAC,CAUrB"}
1
+ {"version":3,"file":"definePackagePolicy.d.ts","sourceRoot":"","sources":["../../src/policyDefiners/definePackagePolicy.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,KAAK,EACX,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,EACnB,MAAM,cAAc,CAAC;AAItB;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,EAAE,CAAC,IAAI,CACtC,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,uBAAuB,CAAC,CAAC,CAAC,KAC5B,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAElC;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,GAAG,WAAW,EAAE,CAAC,GAAG,SAAS,EACjE,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,GAErC,gBAAgB,CAAC,CAAC,CAAC,CAUrB"}
@@ -1,4 +1,5 @@
1
1
  import jsonfile from "jsonfile";
2
+ import { resolve } from "pathe";
2
3
  import { PackageJsonRegexMatch } from "../policies/constants.js";
3
4
  const { readFile: readJson } = jsonfile;
4
5
  /**
@@ -12,7 +13,7 @@ export function definePackagePolicy(name, packagePolicy) {
12
13
  name,
13
14
  match: PackageJsonRegexMatch,
14
15
  handler: async (innerArgs) => {
15
- const json = await readJson(innerArgs.file);
16
+ const json = await readJson(resolve(innerArgs.root, innerArgs.file));
16
17
  return packagePolicy(json, innerArgs);
17
18
  },
18
19
  };
@@ -1 +1 @@
1
- {"version":3,"file":"definePackagePolicy.js","sourceRoot":"","sources":["../../src/policyDefiners/definePackagePolicy.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAOjE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC;AAYxC;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAClC,IAAY,EACZ,aAAuC;IAGvC,0CAA0C;IAC1C,OAAO;QACN,IAAI;QACJ,KAAK,EAAE,qBAAqB;QAC5B,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;YAC5B,MAAM,IAAI,GAAM,MAAM,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC/C,OAAO,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACvC,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import jsonfile from \"jsonfile\";\nimport type { PackageJson } from \"type-fest\";\nimport { PackageJsonRegexMatch } from \"../policies/constants.js\";\nimport type {\n\tPolicyDefinition,\n\tPolicyFunctionArguments,\n\tPolicyHandlerResult,\n} from \"../policy.js\";\n\nconst { readFile: readJson } = jsonfile;\n\n/**\n * A policy handler especially for policies that target package.json.\n *\n * @alpha\n */\nexport type PackageJsonHandler<J, C> = (\n\tjson: J,\n\targs: PolicyFunctionArguments<C>,\n) => Promise<PolicyHandlerResult>;\n\n/**\n * Define a repo policy for package.json files.\n *\n * @alpha\n */\nexport function definePackagePolicy<J = PackageJson, C = undefined>(\n\tname: string,\n\tpackagePolicy: PackageJsonHandler<J, C>,\n\t// args: PolicyFunctionArguments<C>,\n): PolicyDefinition<C> {\n\t// const func = () => handler(json, args);\n\treturn {\n\t\tname,\n\t\tmatch: PackageJsonRegexMatch,\n\t\thandler: async (innerArgs) => {\n\t\t\tconst json: J = await readJson(innerArgs.file);\n\t\t\treturn packagePolicy(json, innerArgs);\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"definePackagePolicy.js","sourceRoot":"","sources":["../../src/policyDefiners/definePackagePolicy.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAOjE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC;AAYxC;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAClC,IAAY,EACZ,aAAuC;IAGvC,0CAA0C;IAC1C,OAAO;QACN,IAAI;QACJ,KAAK,EAAE,qBAAqB;QAC5B,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;YAC5B,MAAM,IAAI,GAAM,MAAM,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YACxE,OAAO,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACvC,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import jsonfile from \"jsonfile\";\nimport { resolve } from \"pathe\";\nimport type { PackageJson } from \"type-fest\";\nimport { PackageJsonRegexMatch } from \"../policies/constants.js\";\nimport type {\n\tPolicyDefinition,\n\tPolicyFunctionArguments,\n\tPolicyHandlerResult,\n} from \"../policy.js\";\n\nconst { readFile: readJson } = jsonfile;\n\n/**\n * A policy handler especially for policies that target package.json.\n *\n * @alpha\n */\nexport type PackageJsonHandler<J, C> = (\n\tjson: J,\n\targs: PolicyFunctionArguments<C>,\n) => Promise<PolicyHandlerResult>;\n\n/**\n * Define a repo policy for package.json files.\n *\n * @alpha\n */\nexport function definePackagePolicy<J = PackageJson, C = undefined>(\n\tname: string,\n\tpackagePolicy: PackageJsonHandler<J, C>,\n\t// args: PolicyFunctionArguments<C>,\n): PolicyDefinition<C> {\n\t// const func = () => handler(json, args);\n\treturn {\n\t\tname,\n\t\tmatch: PackageJsonRegexMatch,\n\t\thandler: async (innerArgs) => {\n\t\t\tconst json: J = await readJson(resolve(innerArgs.root, innerArgs.file));\n\t\t\treturn packagePolicy(json, innerArgs);\n\t\t},\n\t};\n}\n"]}
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.54.0"
8
+ "packageVersion": "7.55.1"
9
9
  }
10
10
  ]
11
11
  }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Represents detected indentation style.
3
+ *
4
+ * - `"\t"` - Tab character
5
+ * - `number` - Number of spaces (e.g., 2, 4)
6
+ */
7
+ export type IndentationStyle = "\t" | number;
8
+ /**
9
+ * Default indentation style used when detection fails or no indentation is found.
10
+ *
11
+ * @alpha
12
+ */
13
+ export declare const DEFAULT_INDENTATION: IndentationStyle;
14
+ /**
15
+ * Detect the indentation style used in a file.
16
+ *
17
+ * Analyzes the file content to determine whether tabs or spaces are used
18
+ * for indentation. This is useful when writing files to preserve
19
+ * the existing formatting style.
20
+ *
21
+ * @param filePath - Path to the file
22
+ * @param defaultIndentation - Indentation to use if detection fails (default: tab)
23
+ * @returns The detected indentation (tab character or number of spaces)
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const indent = await detectIndentation("/path/to/package.json");
28
+ * // Returns "\t" for tabs, or a number like 2 or 4 for spaces
29
+ *
30
+ * // With custom default
31
+ * const indent = await detectIndentation("/path/to/file.yaml", 2);
32
+ * ```
33
+ *
34
+ * @alpha
35
+ */
36
+ export declare function detectIndentation(filePath: string, defaultIndentation?: IndentationStyle): Promise<IndentationStyle>;
37
+ /**
38
+ * Detect the indentation style used in text content.
39
+ *
40
+ * Analyzes the content string to determine whether tabs or spaces are used
41
+ * for indentation. This is a pure function useful for testing and for
42
+ * cases where the content is already in memory.
43
+ *
44
+ * The detection algorithm:
45
+ * 1. Splits content into lines
46
+ * 2. Finds the first line that starts with whitespace
47
+ * 3. If it starts with a tab, returns tab
48
+ * 4. If it starts with spaces, returns the count of leading spaces
49
+ * 5. If no indented lines found, returns the default
50
+ *
51
+ * @param content - The file content as a string
52
+ * @param defaultIndentation - Indentation to use if no indented lines found (default: tab)
53
+ * @returns The detected indentation (tab character or number of spaces)
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * // JSON with 2-space indentation
58
+ * const json = '{\n "name": "test"\n}';
59
+ * detectIndentationFromContent(json); // Returns 2
60
+ *
61
+ * // YAML with tab indentation
62
+ * const yaml = 'root:\n\tchild: value';
63
+ * detectIndentationFromContent(yaml); // Returns "\t"
64
+ *
65
+ * // No indentation found - uses default
66
+ * detectIndentationFromContent('no indent'); // Returns "\t"
67
+ * detectIndentationFromContent('no indent', 4); // Returns 4
68
+ * ```
69
+ *
70
+ * @alpha
71
+ */
72
+ export declare function detectIndentationFromContent(content: string, defaultIndentation?: IndentationStyle): IndentationStyle;
73
+ //# sourceMappingURL=indentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indentation.d.ts","sourceRoot":"","sources":["../../src/utils/indentation.ts"],"names":[],"mappings":"AAOA;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,MAAM,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,gBAAuB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,iBAAiB,CACtC,QAAQ,EAAE,MAAM,EAChB,kBAAkB,GAAE,gBAAsC,GACxD,OAAO,CAAC,gBAAgB,CAAC,CAQ3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,4BAA4B,CAC3C,OAAO,EAAE,MAAM,EACf,kBAAkB,GAAE,gBAAsC,GACxD,gBAAgB,CAmBlB"}
@@ -0,0 +1,97 @@
1
+ import { readFile } from "node:fs/promises";
2
+ /**
3
+ * Regex to match leading whitespace at the start of a line.
4
+ */
5
+ const LEADING_WHITESPACE_REGEX = /^(\s+)/;
6
+ /**
7
+ * Default indentation style used when detection fails or no indentation is found.
8
+ *
9
+ * @alpha
10
+ */
11
+ export const DEFAULT_INDENTATION = "\t";
12
+ /**
13
+ * Detect the indentation style used in a file.
14
+ *
15
+ * Analyzes the file content to determine whether tabs or spaces are used
16
+ * for indentation. This is useful when writing files to preserve
17
+ * the existing formatting style.
18
+ *
19
+ * @param filePath - Path to the file
20
+ * @param defaultIndentation - Indentation to use if detection fails (default: tab)
21
+ * @returns The detected indentation (tab character or number of spaces)
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const indent = await detectIndentation("/path/to/package.json");
26
+ * // Returns "\t" for tabs, or a number like 2 or 4 for spaces
27
+ *
28
+ * // With custom default
29
+ * const indent = await detectIndentation("/path/to/file.yaml", 2);
30
+ * ```
31
+ *
32
+ * @alpha
33
+ */
34
+ export async function detectIndentation(filePath, defaultIndentation = DEFAULT_INDENTATION) {
35
+ try {
36
+ const content = await readFile(filePath, "utf-8");
37
+ return detectIndentationFromContent(content, defaultIndentation);
38
+ }
39
+ catch {
40
+ // If we can't read the file, use default
41
+ return defaultIndentation;
42
+ }
43
+ }
44
+ /**
45
+ * Detect the indentation style used in text content.
46
+ *
47
+ * Analyzes the content string to determine whether tabs or spaces are used
48
+ * for indentation. This is a pure function useful for testing and for
49
+ * cases where the content is already in memory.
50
+ *
51
+ * The detection algorithm:
52
+ * 1. Splits content into lines
53
+ * 2. Finds the first line that starts with whitespace
54
+ * 3. If it starts with a tab, returns tab
55
+ * 4. If it starts with spaces, returns the count of leading spaces
56
+ * 5. If no indented lines found, returns the default
57
+ *
58
+ * @param content - The file content as a string
59
+ * @param defaultIndentation - Indentation to use if no indented lines found (default: tab)
60
+ * @returns The detected indentation (tab character or number of spaces)
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * // JSON with 2-space indentation
65
+ * const json = '{\n "name": "test"\n}';
66
+ * detectIndentationFromContent(json); // Returns 2
67
+ *
68
+ * // YAML with tab indentation
69
+ * const yaml = 'root:\n\tchild: value';
70
+ * detectIndentationFromContent(yaml); // Returns "\t"
71
+ *
72
+ * // No indentation found - uses default
73
+ * detectIndentationFromContent('no indent'); // Returns "\t"
74
+ * detectIndentationFromContent('no indent', 4); // Returns 4
75
+ * ```
76
+ *
77
+ * @alpha
78
+ */
79
+ export function detectIndentationFromContent(content, defaultIndentation = DEFAULT_INDENTATION) {
80
+ const lines = content.split("\n");
81
+ // Look for the first indented line to detect the pattern
82
+ for (const line of lines) {
83
+ const match = LEADING_WHITESPACE_REGEX.exec(line);
84
+ if (match?.[1]) {
85
+ const indent = match[1];
86
+ // Check if it's tabs
87
+ if (indent.startsWith("\t")) {
88
+ return "\t";
89
+ }
90
+ // Count spaces
91
+ return indent.length;
92
+ }
93
+ }
94
+ // No indented lines found, use default
95
+ return defaultIndentation;
96
+ }
97
+ //# sourceMappingURL=indentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indentation.js","sourceRoot":"","sources":["../../src/utils/indentation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C;;GAEG;AACH,MAAM,wBAAwB,GAAG,QAAQ,CAAC;AAU1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAqB,IAAI,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,QAAgB,EAChB,qBAAuC,mBAAmB;IAE1D,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,4BAA4B,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACR,yCAAyC;QACzC,OAAO,kBAAkB,CAAC;IAC3B,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,4BAA4B,CAC3C,OAAe,EACf,qBAAuC,mBAAmB;IAE1D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAElC,yDAAyD;IACzD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,qBAAqB;YACrB,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7B,OAAO,IAAI,CAAC;YACb,CAAC;YACD,eAAe;YACf,OAAO,MAAM,CAAC,MAAM,CAAC;QACtB,CAAC;IACF,CAAC;IAED,uCAAuC;IACvC,OAAO,kBAAkB,CAAC;AAC3B,CAAC","sourcesContent":["import { readFile } from \"node:fs/promises\";\n\n/**\n * Regex to match leading whitespace at the start of a line.\n */\nconst LEADING_WHITESPACE_REGEX = /^(\\s+)/;\n\n/**\n * Represents detected indentation style.\n *\n * - `\"\\t\"` - Tab character\n * - `number` - Number of spaces (e.g., 2, 4)\n */\nexport type IndentationStyle = \"\\t\" | number;\n\n/**\n * Default indentation style used when detection fails or no indentation is found.\n *\n * @alpha\n */\nexport const DEFAULT_INDENTATION: IndentationStyle = \"\\t\";\n\n/**\n * Detect the indentation style used in a file.\n *\n * Analyzes the file content to determine whether tabs or spaces are used\n * for indentation. This is useful when writing files to preserve\n * the existing formatting style.\n *\n * @param filePath - Path to the file\n * @param defaultIndentation - Indentation to use if detection fails (default: tab)\n * @returns The detected indentation (tab character or number of spaces)\n *\n * @example\n * ```typescript\n * const indent = await detectIndentation(\"/path/to/package.json\");\n * // Returns \"\\t\" for tabs, or a number like 2 or 4 for spaces\n *\n * // With custom default\n * const indent = await detectIndentation(\"/path/to/file.yaml\", 2);\n * ```\n *\n * @alpha\n */\nexport async function detectIndentation(\n\tfilePath: string,\n\tdefaultIndentation: IndentationStyle = DEFAULT_INDENTATION,\n): Promise<IndentationStyle> {\n\ttry {\n\t\tconst content = await readFile(filePath, \"utf-8\");\n\t\treturn detectIndentationFromContent(content, defaultIndentation);\n\t} catch {\n\t\t// If we can't read the file, use default\n\t\treturn defaultIndentation;\n\t}\n}\n\n/**\n * Detect the indentation style used in text content.\n *\n * Analyzes the content string to determine whether tabs or spaces are used\n * for indentation. This is a pure function useful for testing and for\n * cases where the content is already in memory.\n *\n * The detection algorithm:\n * 1. Splits content into lines\n * 2. Finds the first line that starts with whitespace\n * 3. If it starts with a tab, returns tab\n * 4. If it starts with spaces, returns the count of leading spaces\n * 5. If no indented lines found, returns the default\n *\n * @param content - The file content as a string\n * @param defaultIndentation - Indentation to use if no indented lines found (default: tab)\n * @returns The detected indentation (tab character or number of spaces)\n *\n * @example\n * ```typescript\n * // JSON with 2-space indentation\n * const json = '{\\n \"name\": \"test\"\\n}';\n * detectIndentationFromContent(json); // Returns 2\n *\n * // YAML with tab indentation\n * const yaml = 'root:\\n\\tchild: value';\n * detectIndentationFromContent(yaml); // Returns \"\\t\"\n *\n * // No indentation found - uses default\n * detectIndentationFromContent('no indent'); // Returns \"\\t\"\n * detectIndentationFromContent('no indent', 4); // Returns 4\n * ```\n *\n * @alpha\n */\nexport function detectIndentationFromContent(\n\tcontent: string,\n\tdefaultIndentation: IndentationStyle = DEFAULT_INDENTATION,\n): IndentationStyle {\n\tconst lines = content.split(\"\\n\");\n\n\t// Look for the first indented line to detect the pattern\n\tfor (const line of lines) {\n\t\tconst match = LEADING_WHITESPACE_REGEX.exec(line);\n\t\tif (match?.[1]) {\n\t\t\tconst indent = match[1];\n\t\t\t// Check if it's tabs\n\t\t\tif (indent.startsWith(\"\\t\")) {\n\t\t\t\treturn \"\\t\";\n\t\t\t}\n\t\t\t// Count spaces\n\t\t\treturn indent.length;\n\t\t}\n\t}\n\n\t// No indented lines found, use default\n\treturn defaultIndentation;\n}\n"]}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Utility functions for working with npm package names.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ /**
7
+ * Result of parsing a package name into its components.
8
+ *
9
+ * @alpha
10
+ */
11
+ export interface ParsedPackageName {
12
+ /**
13
+ * The full original package name.
14
+ */
15
+ name: string;
16
+ /**
17
+ * The scope portion of the name (e.g., `@myorg`), or `undefined` if unscoped.
18
+ */
19
+ scope: string | undefined;
20
+ /**
21
+ * The name portion without the scope (e.g., `my-package`).
22
+ * For unscoped packages, this is the same as `name`.
23
+ */
24
+ unscopedName: string;
25
+ }
26
+ /**
27
+ * Parse a package name into its scope and unscoped name components.
28
+ *
29
+ * @param packageName - The package name to parse (e.g., `@myorg/my-package` or `my-package`)
30
+ * @returns An object containing the parsed components
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * parsePackageName("@myorg/my-package")
35
+ * // Returns: { name: "@myorg/my-package", scope: "@myorg", unscopedName: "my-package" }
36
+ *
37
+ * parsePackageName("my-package")
38
+ * // Returns: { name: "my-package", scope: undefined, unscopedName: "my-package" }
39
+ * ```
40
+ *
41
+ * @alpha
42
+ */
43
+ export declare function parsePackageName(packageName: string): ParsedPackageName;
44
+ /**
45
+ * Extract just the scope from a package name.
46
+ *
47
+ * @param packageName - The package name (e.g., `@myorg/foo` or `unscoped-pkg`)
48
+ * @returns The scope (e.g., `@myorg`) or `undefined` if unscoped
49
+ *
50
+ * @alpha
51
+ */
52
+ export declare function getPackageScope(packageName: string): string | undefined;
53
+ /**
54
+ * Extract the unscoped portion of a package name.
55
+ *
56
+ * @param packageName - The package name (e.g., `@myorg/my-package` or `my-package`)
57
+ * @returns The unscoped name (e.g., `my-package`)
58
+ *
59
+ * @alpha
60
+ */
61
+ export declare function getUnscopedName(packageName: string): string;
62
+ /**
63
+ * Check if a package name is scoped.
64
+ *
65
+ * @param packageName - The package name to check
66
+ * @returns `true` if the package is scoped (starts with `@` and contains `/`)
67
+ *
68
+ * @alpha
69
+ */
70
+ export declare function isScoped(packageName: string): boolean;
71
+ //# sourceMappingURL=packageName.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packageName.d.ts","sourceRoot":"","sources":["../../src/utils/packageName.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAE1B;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,CAwBvE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEvE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAErD"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Utility functions for working with npm package names.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ /**
7
+ * Parse a package name into its scope and unscoped name components.
8
+ *
9
+ * @param packageName - The package name to parse (e.g., `@myorg/my-package` or `my-package`)
10
+ * @returns An object containing the parsed components
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * parsePackageName("@myorg/my-package")
15
+ * // Returns: { name: "@myorg/my-package", scope: "@myorg", unscopedName: "my-package" }
16
+ *
17
+ * parsePackageName("my-package")
18
+ * // Returns: { name: "my-package", scope: undefined, unscopedName: "my-package" }
19
+ * ```
20
+ *
21
+ * @alpha
22
+ */
23
+ export function parsePackageName(packageName) {
24
+ if (!packageName.startsWith("@")) {
25
+ return {
26
+ name: packageName,
27
+ scope: undefined,
28
+ unscopedName: packageName,
29
+ };
30
+ }
31
+ const slashIndex = packageName.indexOf("/");
32
+ if (slashIndex === -1) {
33
+ // Malformed scoped name without a slash - treat as unscoped
34
+ return {
35
+ name: packageName,
36
+ scope: undefined,
37
+ unscopedName: packageName,
38
+ };
39
+ }
40
+ return {
41
+ name: packageName,
42
+ scope: packageName.slice(0, slashIndex),
43
+ unscopedName: packageName.slice(slashIndex + 1),
44
+ };
45
+ }
46
+ /**
47
+ * Extract just the scope from a package name.
48
+ *
49
+ * @param packageName - The package name (e.g., `@myorg/foo` or `unscoped-pkg`)
50
+ * @returns The scope (e.g., `@myorg`) or `undefined` if unscoped
51
+ *
52
+ * @alpha
53
+ */
54
+ export function getPackageScope(packageName) {
55
+ return parsePackageName(packageName).scope;
56
+ }
57
+ /**
58
+ * Extract the unscoped portion of a package name.
59
+ *
60
+ * @param packageName - The package name (e.g., `@myorg/my-package` or `my-package`)
61
+ * @returns The unscoped name (e.g., `my-package`)
62
+ *
63
+ * @alpha
64
+ */
65
+ export function getUnscopedName(packageName) {
66
+ return parsePackageName(packageName).unscopedName;
67
+ }
68
+ /**
69
+ * Check if a package name is scoped.
70
+ *
71
+ * @param packageName - The package name to check
72
+ * @returns `true` if the package is scoped (starts with `@` and contains `/`)
73
+ *
74
+ * @alpha
75
+ */
76
+ export function isScoped(packageName) {
77
+ return parsePackageName(packageName).scope !== undefined;
78
+ }
79
+ //# sourceMappingURL=packageName.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packageName.js","sourceRoot":"","sources":["../../src/utils/packageName.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAyBH;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IACnD,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO;YACN,IAAI,EAAE,WAAW;YACjB,KAAK,EAAE,SAAS;YAChB,YAAY,EAAE,WAAW;SACzB,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;QACvB,4DAA4D;QAC5D,OAAO;YACN,IAAI,EAAE,WAAW;YACjB,KAAK,EAAE,SAAS;YAChB,YAAY,EAAE,WAAW;SACzB,CAAC;IACH,CAAC;IAED,OAAO;QACN,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC;QACvC,YAAY,EAAE,WAAW,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC;KAC/C,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,WAAmB;IAClD,OAAO,gBAAgB,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC;AAC5C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,WAAmB;IAClD,OAAO,gBAAgB,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC;AACnD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CAAC,WAAmB;IAC3C,OAAO,gBAAgB,CAAC,WAAW,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC;AAC1D,CAAC","sourcesContent":["/**\n * Utility functions for working with npm package names.\n *\n * @packageDocumentation\n */\n\n/**\n * Result of parsing a package name into its components.\n *\n * @alpha\n */\nexport interface ParsedPackageName {\n\t/**\n\t * The full original package name.\n\t */\n\tname: string;\n\n\t/**\n\t * The scope portion of the name (e.g., `@myorg`), or `undefined` if unscoped.\n\t */\n\tscope: string | undefined;\n\n\t/**\n\t * The name portion without the scope (e.g., `my-package`).\n\t * For unscoped packages, this is the same as `name`.\n\t */\n\tunscopedName: string;\n}\n\n/**\n * Parse a package name into its scope and unscoped name components.\n *\n * @param packageName - The package name to parse (e.g., `@myorg/my-package` or `my-package`)\n * @returns An object containing the parsed components\n *\n * @example\n * ```typescript\n * parsePackageName(\"@myorg/my-package\")\n * // Returns: { name: \"@myorg/my-package\", scope: \"@myorg\", unscopedName: \"my-package\" }\n *\n * parsePackageName(\"my-package\")\n * // Returns: { name: \"my-package\", scope: undefined, unscopedName: \"my-package\" }\n * ```\n *\n * @alpha\n */\nexport function parsePackageName(packageName: string): ParsedPackageName {\n\tif (!packageName.startsWith(\"@\")) {\n\t\treturn {\n\t\t\tname: packageName,\n\t\t\tscope: undefined,\n\t\t\tunscopedName: packageName,\n\t\t};\n\t}\n\n\tconst slashIndex = packageName.indexOf(\"/\");\n\tif (slashIndex === -1) {\n\t\t// Malformed scoped name without a slash - treat as unscoped\n\t\treturn {\n\t\t\tname: packageName,\n\t\t\tscope: undefined,\n\t\t\tunscopedName: packageName,\n\t\t};\n\t}\n\n\treturn {\n\t\tname: packageName,\n\t\tscope: packageName.slice(0, slashIndex),\n\t\tunscopedName: packageName.slice(slashIndex + 1),\n\t};\n}\n\n/**\n * Extract just the scope from a package name.\n *\n * @param packageName - The package name (e.g., `@myorg/foo` or `unscoped-pkg`)\n * @returns The scope (e.g., `@myorg`) or `undefined` if unscoped\n *\n * @alpha\n */\nexport function getPackageScope(packageName: string): string | undefined {\n\treturn parsePackageName(packageName).scope;\n}\n\n/**\n * Extract the unscoped portion of a package name.\n *\n * @param packageName - The package name (e.g., `@myorg/my-package` or `my-package`)\n * @returns The unscoped name (e.g., `my-package`)\n *\n * @alpha\n */\nexport function getUnscopedName(packageName: string): string {\n\treturn parsePackageName(packageName).unscopedName;\n}\n\n/**\n * Check if a package name is scoped.\n *\n * @param packageName - The package name to check\n * @returns `true` if the package is scoped (starts with `@` and contains `/`)\n *\n * @alpha\n */\nexport function isScoped(packageName: string): boolean {\n\treturn parsePackageName(packageName).scope !== undefined;\n}\n"]}
@@ -125,5 +125,5 @@
125
125
  ]
126
126
  }
127
127
  },
128
- "version": "0.7.1"
128
+ "version": "0.8.0"
129
129
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repopo",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "Enforce policies on all or some of the files in a git repository.",
5
5
  "keywords": [
6
6
  "policy",
@@ -42,46 +42,60 @@
42
42
  "/THIRD-PARTY-LICENSES.txt"
43
43
  ],
44
44
  "oclif": {
45
+ "additionalHelpFlags": [
46
+ "-h"
47
+ ],
48
+ "additionalVersionFlags": [
49
+ "-V"
50
+ ],
45
51
  "bin": "repopo",
46
52
  "commands": {
47
53
  "strategy": "pattern",
48
54
  "target": "./esm/commands"
49
55
  },
50
- "devPlugins": [],
56
+ "devPlugins": [
57
+ "@oclif/plugin-command-snapshot"
58
+ ],
51
59
  "dirname": "repopo",
52
60
  "plugins": [],
53
61
  "repositoryPrefix": "<%- repo %>/blob/main/packages/repopo/<%- commandPath %>"
54
62
  },
55
63
  "dependencies": {
56
- "@fluid-tools/build-infrastructure": "~0.60.0",
64
+ "@fluid-tools/build-infrastructure": "~0.63.0",
57
65
  "@oclif/core": "^4.8.0",
58
- "@oclif/plugin-help": "^6.2.35",
59
- "@rushstack/node-core-library": "^5.18.0",
66
+ "@oclif/plugin-help": "^6.2.36",
67
+ "@rushstack/node-core-library": "^5.19.0",
68
+ "consola": "^3.4.0",
60
69
  "defu": "^6.1.4",
61
70
  "jsonfile": "^6.2.0",
62
71
  "microdiff": "^1.5.0",
63
72
  "pathe": "^2.0.3",
64
73
  "picocolors": "^1.1.1",
74
+ "picomatch": "^4.0.3",
75
+ "resolve-workspace-root": "^2.0.0",
65
76
  "simple-git": "^3.30.0",
66
- "@tylerbu/cli-api": "^0.9.0",
77
+ "tinyglobby": "^0.2.15",
78
+ "@tylerbu/cli-api": "^0.10.0",
67
79
  "@tylerbu/fundamentals": "^0.3.0"
68
80
  },
69
81
  "devDependencies": {
70
- "@biomejs/biome": "2.2.5",
71
- "@microsoft/api-extractor": "^7.54.0",
82
+ "@biomejs/biome": "2.3.8",
83
+ "@microsoft/api-extractor": "^7.55.1",
72
84
  "@oclif/plugin-command-snapshot": "^5.3.8",
73
- "@oclif/test": "^4.1.14",
85
+ "@oclif/test": "^4.1.15",
74
86
  "@types/jsonfile": "^6.1.4",
75
- "@types/node": "^20.19.24",
87
+ "@types/node": "^20.19.25",
88
+ "@types/picomatch": "^4.0.2",
76
89
  "@types/tmp": "^0.2.6",
77
- "generate-license-file": "^4.1.0",
78
- "oclif": "^4.22.44",
79
- "rimraf": "^6.1.0",
90
+ "@vitest/coverage-v8": "^4.0.17",
91
+ "generate-license-file": "^4.1.1",
92
+ "oclif": "^4.22.50",
93
+ "rimraf": "^6.1.2",
80
94
  "tmp-promise": "^3.0.3",
81
95
  "ts-node": "^10.9.2",
82
- "type-fest": "^5.2.0",
96
+ "type-fest": "^5.4.1",
83
97
  "typescript": "~5.9.3",
84
- "vitest": "^3.2.4"
98
+ "vitest": "^4.0.17"
85
99
  },
86
100
  "peerDependencies": {
87
101
  "sort-package-json": "*"
@@ -120,6 +134,7 @@
120
134
  "readme:main": "oclif readme --no-aliases",
121
135
  "release:license": "generate-license-file -c ../../.generatelicensefile.cjs",
122
136
  "test": "vitest run",
137
+ "test:coverage": "vitest run --coverage",
123
138
  "test:snapshots": "./bin/dev.js snapshot:compare --filepath test/commands/__snapshots__/commands.json"
124
139
  }
125
140
  }