@ankhorage/paradox 0.1.21 → 0.1.22

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.22
4
+
5
+ ### Patch Changes
6
+
7
+ - 125eac9: Add opt-in GitHub Sponsors support from a single root-level `donation.account` config, including generated Donation documentation and `.github/FUNDING.yml` handling.
8
+
3
9
  ## 0.1.21
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # @ankhorage/paradox
5
5
 
6
- ![license: MIT](./paradox/badges/license.svg) ![npm: v0.1.21](./paradox/badges/npm.svg) ![runtime: bun](./paradox/badges/runtime.svg) ![typescript: strict](./paradox/badges/typescript.svg) ![eslint: checked](./paradox/badges/eslint.svg) ![prettier: checked](./paradox/badges/prettier.svg) ![build: checked](./paradox/badges/build.svg) ![tests: checked](./paradox/badges/tests.svg) ![docs: paradox](./paradox/badges/docs.svg)
6
+ ![license: MIT](./paradox/badges/license.svg) ![npm: v0.1.22](./paradox/badges/npm.svg) ![runtime: bun](./paradox/badges/runtime.svg) ![typescript: strict](./paradox/badges/typescript.svg) ![eslint: checked](./paradox/badges/eslint.svg) ![prettier: checked](./paradox/badges/prettier.svg) ![build: checked](./paradox/badges/build.svg) ![tests: checked](./paradox/badges/tests.svg) ![docs: paradox](./paradox/badges/docs.svg)
7
7
 
8
8
  Deterministic documentation generator for TypeScript packages.
9
9
 
@@ -70,6 +70,10 @@ import { defineParadoxConfig } from './src/config/defineParadoxConfig.js';
70
70
  export default defineParadoxConfig({
71
71
  mode: 'write',
72
72
 
73
+ donation: {
74
+ account: 'ankhorage',
75
+ },
76
+
73
77
  docs: {
74
78
  title: '@ankhorage/paradox',
75
79
  description: 'Deterministic documentation generator for TypeScript packages.',
@@ -88,12 +92,13 @@ export default defineParadoxConfig({
88
92
  <details>
89
93
  <summary>Configuration options</summary>
90
94
 
91
- | Field | Type | Required | Default | Description |
92
- | ------- | ------------------------------------------------------------------------------------------------------------------- | -------- | ------- | ----------- |
93
- | mode | `'safe' \| 'write' \| undefined` | no | — | |
94
- | docs | `{ title?: string; description?: string; usage?: { description?: string; entrypoints?: string[]; }; } \| undefined` | no | — | |
95
- | package | `{ root?: string; entrypoints?: string[]; } \| undefined` | no | — | |
96
- | output | `{ dir?: string; } \| undefined` | no | — | |
95
+ | Field | Type | Required | Default | Description |
96
+ | -------- | ------------------------------------------------------------------------------------------------------------------- | -------- | ------- | ----------- |
97
+ | mode | `'safe' \| 'write' \| undefined` | no | — | |
98
+ | donation | `{ account: string; } \| undefined` | no | — | |
99
+ | docs | `{ title?: string; description?: string; usage?: { description?: string; entrypoints?: string[]; }; } \| undefined` | no | — | |
100
+ | package | `{ root?: string; entrypoints?: string[]; } \| undefined` | no | — | |
101
+ | output | `{ dir?: string; } \| undefined` | no | — | |
97
102
 
98
103
  </details>
99
104
 
@@ -136,3 +141,9 @@ Module: `src/config/types.ts`
136
141
  Source: `src/config/types.ts:7:1`
137
142
 
138
143
  </details>
144
+
145
+ ## Donation
146
+
147
+ If this project is useful to you, you can support its continued development.
148
+
149
+ [Support @ankhorage](https://github.com/sponsors/ankhorage)
@@ -1,5 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
+ import { validateDonationAccount } from '../config/utils/validateDonationAccount.js';
3
4
  import { analyzeBadges } from './badges.js';
4
5
  import { analyzeComponents } from './components.js';
5
6
  import { analyzeExports } from './exports.js';
@@ -20,6 +21,9 @@ import { createUsageFromPackageJson } from './usage.js';
20
21
  export async function analyze(config, runtime) {
21
22
  const root = runtime.packageRoot;
22
23
  const pkg = await readPackageJson(root);
24
+ const donation = config.donation === undefined
25
+ ? null
26
+ : { account: validateDonationAccount(config.donation.account) };
23
27
  const usage = createUsageFromPackageJson(pkg);
24
28
  const badges = await analyzeBadges(root, pkg);
25
29
  const project = createProject(root);
@@ -67,6 +71,7 @@ export async function analyze(config, runtime) {
67
71
  packageName: config.docs?.title ?? pkg.name,
68
72
  packageId: pkg.name,
69
73
  description: config.docs?.description ?? pkg.description ?? null,
74
+ donation,
70
75
  exports,
71
76
  components,
72
77
  sourceFunctions,
@@ -75,6 +75,9 @@ export interface AnalysisUsage {
75
75
  packageName: string;
76
76
  commands: AnalysisUsageCommand[];
77
77
  }
78
+ interface AnalysisDonation {
79
+ account: string;
80
+ }
78
81
  interface AnalysisUsageCommand {
79
82
  name: string;
80
83
  command: string;
@@ -165,6 +168,7 @@ export interface AnalysisResult {
165
168
  packageName: string;
166
169
  packageId: string;
167
170
  description: string | null;
171
+ donation: AnalysisDonation | null;
168
172
  exports: AnalysisExport[];
169
173
  components: AnalysisComponent[];
170
174
  sourceFunctions: AnalysisSourceFunction[];
@@ -6,6 +6,11 @@
6
6
  */
7
7
  export interface ParadoxConfig {
8
8
  mode?: 'safe' | 'write';
9
+ /** Enables canonical GitHub Sponsors integration for generated repository documentation. */
10
+ donation?: {
11
+ /** GitHub Sponsors account login used for the Sponsor button and Donation chapter. */
12
+ account: string;
13
+ };
9
14
  docs?: {
10
15
  title?: string;
11
16
  description?: string;
@@ -0,0 +1 @@
1
+ export declare function validateDonationAccount(account: string): string;
@@ -0,0 +1,7 @@
1
+ export function validateDonationAccount(account) {
2
+ if (!githubAccountPattern.test(account)) {
3
+ throw new Error(`Invalid donation account "${account}". Expected a GitHub account login containing only alphanumeric characters or single hyphens.`);
4
+ }
5
+ return account;
6
+ }
7
+ const githubAccountPattern = /^(?!-)(?!.*--)[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/;
@@ -27,6 +27,9 @@ interface BuildModelInput {
27
27
  packageName: string;
28
28
  packageId: string;
29
29
  description: string | null;
30
+ donation: {
31
+ account: string;
32
+ } | null;
30
33
  badges: {
31
34
  id: string;
32
35
  label: string;
@@ -9,6 +9,7 @@ export function buildModel(analysis) {
9
9
  packageName: analysis.packageName,
10
10
  packageId: analysis.packageId,
11
11
  description: analysis.description,
12
+ donation: analysis.donation === null ? null : { account: analysis.donation.account },
12
13
  badges: analysis.badges.map((badge) => ({
13
14
  id: badge.id,
14
15
  label: badge.label,
@@ -5,6 +5,7 @@ export interface DocumentationModel {
5
5
  packageName: string;
6
6
  packageId: string;
7
7
  description: string | null;
8
+ donation: DonationModel | null;
8
9
  badges: GeneratedBadge[];
9
10
  usage: UsageModel | null;
10
11
  readmeUsageDescription: string | null;
@@ -20,6 +21,9 @@ export interface DocumentationModel {
20
21
  sequenceScenarios: SequenceScenarioModel[];
21
22
  graphs: GraphModel;
22
23
  }
24
+ interface DonationModel {
25
+ account: string;
26
+ }
23
27
  export interface GeneratedBadge {
24
28
  id: string;
25
29
  label: string;
@@ -1,5 +1,7 @@
1
1
  import { renderBadgeArtifacts } from './renderers/badges.js';
2
2
  import { renderDiagramArtifacts } from './renderers/diagrams.js';
3
+ import { renderDonation } from './renderers/donation.js';
4
+ import { renderFundingYaml } from './renderers/funding.js';
3
5
  import { renderHtml } from './renderers/html.js';
4
6
  import { renderMarkdown } from './renderers/markdown.js';
5
7
  /***
@@ -16,6 +18,7 @@ export function render(model, options = {}) {
16
18
  exportsJson: `${JSON.stringify(model.exports, null, 2)}\n`,
17
19
  paradoxJson: `${JSON.stringify(model, null, 2)}\n`,
18
20
  indexHtml: '',
21
+ fundingYaml: renderFundingYaml(model),
19
22
  badges,
20
23
  diagrams,
21
24
  };
@@ -29,5 +32,6 @@ export function render(model, options = {}) {
29
32
  for (const renderer of [renderMarkdown, renderHtml]) {
30
33
  Object.assign(result, renderer(context));
31
34
  }
35
+ Object.assign(result, renderDonation(context));
32
36
  return result;
33
37
  }
@@ -0,0 +1,2 @@
1
+ import type { RenderContext } from '../types.js';
2
+ export declare function renderDonation(context: RenderContext): Pick<RenderContext['result'], 'indexHtml' | 'readme'>;
@@ -0,0 +1,30 @@
1
+ export function renderDonation(context) {
2
+ const { donation } = context.model;
3
+ if (donation === null) {
4
+ return { indexHtml: context.result.indexHtml, readme: context.result.readme };
5
+ }
6
+ const { account } = donation;
7
+ return {
8
+ readme: renderDonationReadme(context.result.readme, account),
9
+ indexHtml: renderDonationHtml(context.result.indexHtml, account),
10
+ };
11
+ }
12
+ function renderDonationHtml(indexHtml, account) {
13
+ const mainEnd = indexHtml.indexOf('\n </main>');
14
+ const homeStart = indexHtml.indexOf('<section id="view-home"');
15
+ const homeEnd = indexHtml.lastIndexOf('\n </section>', mainEnd);
16
+ if (mainEnd < 0 || homeStart < 0 || homeEnd < homeStart) {
17
+ throw new Error('Unable to locate Paradox HTML home view for Donation rendering.');
18
+ }
19
+ const panel = [
20
+ ' <section class="panel" data-search="donation sponsor support">',
21
+ ' <h2>Donation</h2>',
22
+ ' <p>If this project is useful to you, you can support its continued development.</p>',
23
+ ` <p><a href="https://github.com/sponsors/${account}">Support @${account}</a></p>`,
24
+ ' </section>',
25
+ ].join('\n');
26
+ return `${indexHtml.slice(0, homeEnd)}\n${panel}${indexHtml.slice(homeEnd)}`;
27
+ }
28
+ function renderDonationReadme(readme, account) {
29
+ return `${readme.trimEnd()}\n\n## Donation\n\nIf this project is useful to you, you can support its continued development.\n\n[Support @${account}](https://github.com/sponsors/${account})\n`;
30
+ }
@@ -0,0 +1,2 @@
1
+ import type { DocumentationModel } from '../../model/types.js';
2
+ export declare function renderFundingYaml(model: DocumentationModel): string | null;
@@ -0,0 +1,9 @@
1
+ export function renderFundingYaml(model) {
2
+ if (model.donation === null)
3
+ return null;
4
+ return [
5
+ '# Generated by Paradox. Do not edit manually.',
6
+ `github: ${model.donation.account}`,
7
+ '',
8
+ ].join('\n');
9
+ }
@@ -18,6 +18,7 @@ export interface RenderResult {
18
18
  exportsJson: string;
19
19
  paradoxJson: string;
20
20
  indexHtml: string;
21
+ fundingYaml: string | null;
21
22
  diagrams: DiagramArtifact[];
22
23
  badges: BadgeArtifact[];
23
24
  }
@@ -0,0 +1 @@
1
+ export declare function syncFundingFileAsync(packageRoot: string, fundingYaml: string | null): Promise<void>;
@@ -0,0 +1,30 @@
1
+ import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
2
+ import { dirname, join } from 'node:path';
3
+ export async function syncFundingFileAsync(packageRoot, fundingYaml) {
4
+ const fundingPath = join(packageRoot, '.github', 'FUNDING.yml');
5
+ const existing = await readFundingFileAsync(fundingPath);
6
+ if (fundingYaml === null) {
7
+ if (existing !== null && isParadoxOwnedFunding(existing))
8
+ await unlink(fundingPath);
9
+ return;
10
+ }
11
+ if (existing !== null && !isParadoxOwnedFunding(existing)) {
12
+ throw new Error('Refusing to overwrite existing non-Paradox .github/FUNDING.yml.');
13
+ }
14
+ await mkdir(dirname(fundingPath), { recursive: true });
15
+ await writeFile(fundingPath, fundingYaml);
16
+ }
17
+ const generatedFundingMarker = '# Generated by Paradox. Do not edit manually.';
18
+ function isParadoxOwnedFunding(content) {
19
+ return content.startsWith(`${generatedFundingMarker}\n`);
20
+ }
21
+ async function readFundingFileAsync(path) {
22
+ try {
23
+ return await readFile(path, 'utf-8');
24
+ }
25
+ catch (error) {
26
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT')
27
+ return null;
28
+ throw error;
29
+ }
30
+ }
@@ -1,5 +1,6 @@
1
1
  import { mkdir, writeFile } from 'node:fs/promises';
2
2
  import { dirname, join } from 'node:path';
3
+ import { syncFundingFileAsync } from './utils/syncFundingFileAsync.js';
3
4
  /***
4
5
  * Writes generated documentation artifacts to the configured output paths.
5
6
  */
@@ -25,5 +26,6 @@ export async function write(result, config, runtime) {
25
26
  }
26
27
  if (mode === 'write') {
27
28
  await writeFile(join(root, 'README.md'), result.readme);
29
+ await syncFundingFileAsync(root, result.fundingYaml);
28
30
  }
29
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/paradox",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": "Deterministic documentation generator for TypeScript packages.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {