@reddoorla/maintenance 0.4.0 → 0.5.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.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as Site, a as AuditResult, A as AuditName, b as RecipeResult, R as RecipeName, I as InventoryProvider } from './sync-configs-C56tK7Go.js';
2
- export { C as ConfigName, c as SyncConfigsOptions, s as syncConfigs } from './sync-configs-C56tK7Go.js';
1
+ import { S as Site, a as AuditResult, A as AuditName, b as RecipeResult, R as RecipeName, I as InventoryProvider } from './sync-configs-CW3nKS_N.js';
2
+ export { C as ConfigName, c as SyncConfigsOptions, s as syncConfigs } from './sync-configs-CW3nKS_N.js';
3
3
 
4
4
  type SpawnResult = {
5
5
  code: number;
package/dist/index.js CHANGED
@@ -725,6 +725,93 @@ function templatesByName(which) {
725
725
  return ALL_TEMPLATES.filter((t) => which.includes(t.config));
726
726
  }
727
727
 
728
+ // src/recipes/sync-configs/gitignore.ts
729
+ var MANAGED_MARKER = "# canonical entries from @reddoorla/maintenance sync-configs";
730
+ var CANONICAL_GITIGNORE_ENTRIES = [
731
+ "node_modules/",
732
+ "build/",
733
+ "dist/",
734
+ ".svelte-kit/",
735
+ "coverage/",
736
+ ".vitest-cache/",
737
+ "playwright-report/",
738
+ "test-results/",
739
+ ".lighthouseci/",
740
+ ".tsbuildinfo",
741
+ ".env",
742
+ ".env.*",
743
+ "!.env.example",
744
+ ".DS_Store",
745
+ "*.log",
746
+ ".vercel/",
747
+ ".netlify/"
748
+ ];
749
+ function stripLeadingSlash(s) {
750
+ return s.startsWith("/") ? s.slice(1) : s;
751
+ }
752
+ function stripTrailingSlash(s) {
753
+ return s.endsWith("/") ? s.slice(0, -1) : s;
754
+ }
755
+ function normalizePresence(line) {
756
+ return stripTrailingSlash(stripLeadingSlash(line.trim()));
757
+ }
758
+ function presentSet(existing) {
759
+ const set = /* @__PURE__ */ new Set();
760
+ for (const raw of existing.split(/\r?\n/)) {
761
+ const trimmed = raw.trim();
762
+ if (!trimmed) continue;
763
+ if (trimmed.startsWith("#")) continue;
764
+ set.add(normalizePresence(trimmed));
765
+ }
766
+ return set;
767
+ }
768
+ function mergeGitignore(existing, canonical) {
769
+ if (existing === null) {
770
+ const body = [MANAGED_MARKER, ...canonical].join("\n") + "\n";
771
+ return { content: body, added: [...canonical] };
772
+ }
773
+ const present = presentSet(existing);
774
+ const added = [];
775
+ for (const entry of canonical) {
776
+ const norm = normalizePresence(entry);
777
+ if (!present.has(norm)) {
778
+ added.push(entry);
779
+ present.add(norm);
780
+ }
781
+ }
782
+ if (added.length === 0) {
783
+ return { content: existing, added: [] };
784
+ }
785
+ let base = existing;
786
+ if (!base.endsWith("\n")) base += "\n";
787
+ const block = ["", MANAGED_MARKER, ...added].join("\n") + "\n";
788
+ return { content: base + block, added };
789
+ }
790
+ function findTrackedArtifacts(tracked, canonical) {
791
+ const dirEntries = [];
792
+ for (const raw of canonical) {
793
+ const t = raw.trim();
794
+ if (!t) continue;
795
+ if (t.startsWith("!")) continue;
796
+ if (/[*?[]/.test(t)) continue;
797
+ const noLead = stripLeadingSlash(t);
798
+ if (!noLead.endsWith("/")) continue;
799
+ const name = stripTrailingSlash(noLead);
800
+ if (!name) continue;
801
+ dirEntries.push(name);
802
+ }
803
+ const matched = [];
804
+ for (const path of tracked) {
805
+ for (const dir of dirEntries) {
806
+ if (path === dir || path.startsWith(dir + "/")) {
807
+ matched.push(path);
808
+ break;
809
+ }
810
+ }
811
+ }
812
+ return matched;
813
+ }
814
+
728
815
  // src/util/git.ts
729
816
  import { execFile } from "child_process";
730
817
  import { promisify } from "util";
@@ -747,6 +834,14 @@ async function createBranch(cwd, name) {
747
834
  async function stageAll(cwd) {
748
835
  await git(cwd, ["add", "-A"]);
749
836
  }
837
+ async function listTrackedFiles(cwd) {
838
+ const { stdout } = await git(cwd, ["ls-files"]);
839
+ return stdout.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
840
+ }
841
+ async function removeFromIndex(cwd, paths) {
842
+ if (paths.length === 0) return;
843
+ await git(cwd, ["rm", "-r", "--cached", "--", ...paths]);
844
+ }
750
845
  async function commit(cwd, message) {
751
846
  await stageAll(cwd);
752
847
  const { stdout: status } = await git(cwd, ["status", "--porcelain"]);
@@ -757,6 +852,7 @@ async function commit(cwd, message) {
757
852
  }
758
853
 
759
854
  // src/recipes/sync-configs.ts
855
+ var GITIGNORE_CONFIG = "gitignore";
760
856
  function siteLabel6(site) {
761
857
  return site.name ?? site.path;
762
858
  }
@@ -767,7 +863,7 @@ async function readMaybe(path) {
767
863
  return null;
768
864
  }
769
865
  }
770
- async function planDiffs(cwd, templates) {
866
+ async function planTemplateDiffs(cwd, templates) {
771
867
  const diffs = [];
772
868
  for (const t of templates) {
773
869
  const existing = await readMaybe(join5(cwd, t.path));
@@ -775,11 +871,29 @@ async function planDiffs(cwd, templates) {
775
871
  }
776
872
  return diffs;
777
873
  }
874
+ async function planGitignore(cwd) {
875
+ const existing = await readMaybe(join5(cwd, ".gitignore"));
876
+ const merge = mergeGitignore(existing, CANONICAL_GITIGNORE_ENTRIES);
877
+ const tracked = await listTrackedFiles(cwd);
878
+ const toUntrack = findTrackedArtifacts(tracked, CANONICAL_GITIGNORE_ENTRIES);
879
+ if (merge.added.length === 0 && toUntrack.length === 0) return { kind: "noop" };
880
+ return { kind: "apply", content: merge.content, toUntrack, added: merge.added };
881
+ }
882
+ async function applyGitignore(cwd, plan) {
883
+ await writeFile3(join5(cwd, ".gitignore"), plan.content, "utf-8");
884
+ if (plan.toUntrack.length > 0) {
885
+ await removeFromIndex(cwd, plan.toUntrack);
886
+ }
887
+ }
778
888
  async function syncConfigs(site, opts = {}) {
779
889
  const label = siteLabel6(site);
780
- const targets = opts.which ? templatesByName(opts.which) : ALL_TEMPLATES;
781
- const diffs = await planDiffs(site.path, targets);
782
- if (diffs.length === 0) {
890
+ const requested = opts.which ?? ALL_TEMPLATES.map((t) => t.config).concat(GITIGNORE_CONFIG);
891
+ const templateNames = requested.filter((c) => c !== GITIGNORE_CONFIG);
892
+ const templates = templatesByName(templateNames);
893
+ const includeGitignore = requested.includes(GITIGNORE_CONFIG);
894
+ const templateDiffs = await planTemplateDiffs(site.path, templates);
895
+ const gitignorePlan = includeGitignore ? await planGitignore(site.path) : { kind: "noop" };
896
+ if (templateDiffs.length === 0 && gitignorePlan.kind === "noop") {
783
897
  return {
784
898
  recipe: "sync-configs",
785
899
  site: label,
@@ -794,7 +908,7 @@ async function syncConfigs(site, opts = {}) {
794
908
  const branch = branchName("sync-configs");
795
909
  await createBranch(site.path, branch);
796
910
  const shas = [];
797
- for (const t of diffs) {
911
+ for (const t of templateDiffs) {
798
912
  await writeFile3(join5(site.path, t.path), t.contents, "utf-8");
799
913
  const sha = await commit(
800
914
  site.path,
@@ -802,6 +916,11 @@ async function syncConfigs(site, opts = {}) {
802
916
  );
803
917
  if (sha) shas.push(sha);
804
918
  }
919
+ if (gitignorePlan.kind === "apply") {
920
+ await applyGitignore(site.path, gitignorePlan);
921
+ const sha = await commit(site.path, `chore: sync gitignore from @reddoorla/maintenance`);
922
+ if (sha) shas.push(sha);
923
+ }
805
924
  return {
806
925
  recipe: "sync-configs",
807
926
  site: label,