@reddoorla/maintenance 0.3.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.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Minimal shape we touch — the full type lives in @sveltejs/kit's `Config`.
3
+ * We don't import it directly to avoid a peer dependency for what amounts
4
+ * to a small config helper. Sites get full type-checking from their own
5
+ * `@sveltejs/kit` install when they invoke createSvelteConfig.
6
+ */
7
+ type WarningFilter = (warning: {
8
+ code?: string;
9
+ message?: string;
10
+ }) => boolean;
11
+ type SvelteConfigLike = {
12
+ kit?: unknown;
13
+ preprocess?: unknown;
14
+ compilerOptions?: {
15
+ warningFilter?: WarningFilter;
16
+ [k: string]: unknown;
17
+ };
18
+ [k: string]: unknown;
19
+ };
20
+ /**
21
+ * Compose a Svelte/Kit config with reddoor's canonical compilerOptions
22
+ * layered in. Sites pass their site-specific bits (`kit.adapter`,
23
+ * `preprocess`, etc.) and get back a complete config with the canonical
24
+ * warning filter applied.
25
+ *
26
+ * If the site supplies its own `compilerOptions.warningFilter`, the filters
27
+ * compose: a warning is shown only when both filters allow it.
28
+ *
29
+ * @example
30
+ * import { createSvelteConfig } from "@reddoorla/maintenance/configs/svelte";
31
+ * import adapter from "@sveltejs/adapter-auto";
32
+ *
33
+ * export default createSvelteConfig({ kit: { adapter: adapter() } });
34
+ */
35
+ declare function createSvelteConfig(siteConfig?: SvelteConfigLike): SvelteConfigLike & {
36
+ compilerOptions: NonNullable<SvelteConfigLike["compilerOptions"]>;
37
+ };
38
+
39
+ export { type SvelteConfigLike, createSvelteConfig, createSvelteConfig as default };
@@ -0,0 +1,30 @@
1
+ // src/configs/svelte.ts
2
+ var SILENCED_WARNING_CODES = /* @__PURE__ */ new Set([
3
+ // `<div ... />` shorthand is widely used across reddoor codebases; the
4
+ // Svelte 5 strictness change for non-void self-closing tags would flood
5
+ // dev logs with no actionable signal.
6
+ "element_invalid_self_closing_tag"
7
+ ]);
8
+ function isSilenced(warning) {
9
+ return warning.code !== void 0 && SILENCED_WARNING_CODES.has(warning.code);
10
+ }
11
+ function createSvelteConfig(siteConfig = {}) {
12
+ const siteCompiler = siteConfig.compilerOptions ?? {};
13
+ const siteFilter = siteCompiler.warningFilter;
14
+ return {
15
+ ...siteConfig,
16
+ compilerOptions: {
17
+ ...siteCompiler,
18
+ warningFilter: (warning) => {
19
+ if (isSilenced(warning)) return false;
20
+ return siteFilter ? siteFilter(warning) : true;
21
+ }
22
+ }
23
+ };
24
+ }
25
+ var svelte_default = createSvelteConfig;
26
+ export {
27
+ createSvelteConfig,
28
+ svelte_default as default
29
+ };
30
+ //# sourceMappingURL=svelte.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/configs/svelte.ts"],"sourcesContent":["/**\n * Minimal shape we touch — the full type lives in @sveltejs/kit's `Config`.\n * We don't import it directly to avoid a peer dependency for what amounts\n * to a small config helper. Sites get full type-checking from their own\n * `@sveltejs/kit` install when they invoke createSvelteConfig.\n */\ntype WarningFilter = (warning: { code?: string; message?: string }) => boolean;\n\nexport type SvelteConfigLike = {\n kit?: unknown;\n preprocess?: unknown;\n compilerOptions?: {\n warningFilter?: WarningFilter;\n [k: string]: unknown;\n };\n [k: string]: unknown;\n};\n\n/** Compiler-level warnings the canonical reddoor stack treats as noise. */\nconst SILENCED_WARNING_CODES = new Set<string>([\n // `<div ... />` shorthand is widely used across reddoor codebases; the\n // Svelte 5 strictness change for non-void self-closing tags would flood\n // dev logs with no actionable signal.\n \"element_invalid_self_closing_tag\",\n]);\n\nfunction isSilenced(warning: { code?: string }): boolean {\n return warning.code !== undefined && SILENCED_WARNING_CODES.has(warning.code);\n}\n\n/**\n * Compose a Svelte/Kit config with reddoor's canonical compilerOptions\n * layered in. Sites pass their site-specific bits (`kit.adapter`,\n * `preprocess`, etc.) and get back a complete config with the canonical\n * warning filter applied.\n *\n * If the site supplies its own `compilerOptions.warningFilter`, the filters\n * compose: a warning is shown only when both filters allow it.\n *\n * @example\n * import { createSvelteConfig } from \"@reddoorla/maintenance/configs/svelte\";\n * import adapter from \"@sveltejs/adapter-auto\";\n *\n * export default createSvelteConfig({ kit: { adapter: adapter() } });\n */\nexport function createSvelteConfig(siteConfig: SvelteConfigLike = {}): SvelteConfigLike & {\n compilerOptions: NonNullable<SvelteConfigLike[\"compilerOptions\"]>;\n} {\n const siteCompiler = siteConfig.compilerOptions ?? {};\n const siteFilter = siteCompiler.warningFilter;\n\n return {\n ...siteConfig,\n compilerOptions: {\n ...siteCompiler,\n warningFilter: (warning) => {\n if (isSilenced(warning)) return false;\n return siteFilter ? siteFilter(warning) : true;\n },\n },\n };\n}\n\nexport default createSvelteConfig;\n"],"mappings":";AAmBA,IAAM,yBAAyB,oBAAI,IAAY;AAAA;AAAA;AAAA;AAAA,EAI7C;AACF,CAAC;AAED,SAAS,WAAW,SAAqC;AACvD,SAAO,QAAQ,SAAS,UAAa,uBAAuB,IAAI,QAAQ,IAAI;AAC9E;AAiBO,SAAS,mBAAmB,aAA+B,CAAC,GAEjE;AACA,QAAM,eAAe,WAAW,mBAAmB,CAAC;AACpD,QAAM,aAAa,aAAa;AAEhC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,iBAAiB;AAAA,MACf,GAAG;AAAA,MACH,eAAe,CAAC,YAAY;AAC1B,YAAI,WAAW,OAAO,EAAG,QAAO;AAChC,eAAO,aAAa,WAAW,OAAO,IAAI;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,iBAAQ;","names":[]}
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-D_Dm-68_.js';
2
- export { C as ConfigName, c as SyncConfigsOptions, s as syncConfigs } from './sync-configs-D_Dm-68_.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
@@ -702,11 +702,116 @@ var playwrightA11y = {
702
702
  contents: `export { default } from "@reddoorla/maintenance/configs/playwright-a11y";
703
703
  `
704
704
  };
705
- var ALL_TEMPLATES = [eslint, prettier, lighthouse, playwrightA11y];
705
+ var svelte = {
706
+ config: "svelte",
707
+ path: "svelte.config.js",
708
+ contents: `import { createSvelteConfig } from "@reddoorla/maintenance/configs/svelte";
709
+ import adapter from "@sveltejs/adapter-auto";
710
+
711
+ /** @type {import('@sveltejs/kit').Config} */
712
+ export default createSvelteConfig({
713
+ kit: { adapter: adapter() },
714
+ });
715
+ `
716
+ };
717
+ var ALL_TEMPLATES = [
718
+ eslint,
719
+ prettier,
720
+ lighthouse,
721
+ playwrightA11y,
722
+ svelte
723
+ ];
706
724
  function templatesByName(which) {
707
725
  return ALL_TEMPLATES.filter((t) => which.includes(t.config));
708
726
  }
709
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
+
710
815
  // src/util/git.ts
711
816
  import { execFile } from "child_process";
712
817
  import { promisify } from "util";
@@ -729,6 +834,14 @@ async function createBranch(cwd, name) {
729
834
  async function stageAll(cwd) {
730
835
  await git(cwd, ["add", "-A"]);
731
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
+ }
732
845
  async function commit(cwd, message) {
733
846
  await stageAll(cwd);
734
847
  const { stdout: status } = await git(cwd, ["status", "--porcelain"]);
@@ -739,6 +852,7 @@ async function commit(cwd, message) {
739
852
  }
740
853
 
741
854
  // src/recipes/sync-configs.ts
855
+ var GITIGNORE_CONFIG = "gitignore";
742
856
  function siteLabel6(site) {
743
857
  return site.name ?? site.path;
744
858
  }
@@ -749,7 +863,7 @@ async function readMaybe(path) {
749
863
  return null;
750
864
  }
751
865
  }
752
- async function planDiffs(cwd, templates) {
866
+ async function planTemplateDiffs(cwd, templates) {
753
867
  const diffs = [];
754
868
  for (const t of templates) {
755
869
  const existing = await readMaybe(join5(cwd, t.path));
@@ -757,11 +871,29 @@ async function planDiffs(cwd, templates) {
757
871
  }
758
872
  return diffs;
759
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
+ }
760
888
  async function syncConfigs(site, opts = {}) {
761
889
  const label = siteLabel6(site);
762
- const targets = opts.which ? templatesByName(opts.which) : ALL_TEMPLATES;
763
- const diffs = await planDiffs(site.path, targets);
764
- 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") {
765
897
  return {
766
898
  recipe: "sync-configs",
767
899
  site: label,
@@ -776,7 +908,7 @@ async function syncConfigs(site, opts = {}) {
776
908
  const branch = branchName("sync-configs");
777
909
  await createBranch(site.path, branch);
778
910
  const shas = [];
779
- for (const t of diffs) {
911
+ for (const t of templateDiffs) {
780
912
  await writeFile3(join5(site.path, t.path), t.contents, "utf-8");
781
913
  const sha = await commit(
782
914
  site.path,
@@ -784,6 +916,11 @@ async function syncConfigs(site, opts = {}) {
784
916
  );
785
917
  if (sha) shas.push(sha);
786
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
+ }
787
924
  return {
788
925
  recipe: "sync-configs",
789
926
  site: label,