@vdaluz/astro-affiliate 1.1.1 → 1.2.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/README.md CHANGED
@@ -110,25 +110,44 @@ Two ways to consume a channel, depending on where the affiliate link lives:
110
110
 
111
111
  ## Markdown links (`remarkAffiliate`)
112
112
 
113
- Wire the plugin into `astro.config.mjs`:
113
+ The plugin runs on the remark/rehype pipeline from `@astrojs/markdown-remark`. Astro 7 no longer
114
+ installs it by default (its default Markdown processor is Sätteri), so install it alongside this
115
+ package:
116
+
117
+ ```sh
118
+ npm install @astrojs/markdown-remark
119
+ ```
120
+
121
+ On Astro 6.4 and later, including Astro 7, pass the plugin to the `unified()` processor in
122
+ `astro.config.mjs`:
114
123
 
115
124
  ```js
125
+ import { unified } from '@astrojs/markdown-remark';
116
126
  import { remarkAffiliate } from '@vdaluz/astro-affiliate/remark';
117
127
  import { affiliate } from './src/config/affiliate';
118
128
 
119
129
  export default defineConfig({
120
130
  markdown: {
121
- remarkPlugins: [[remarkAffiliate, affiliate]],
131
+ processor: unified({
132
+ remarkPlugins: [[remarkAffiliate, affiliate]],
133
+ }),
122
134
  },
123
135
  });
124
136
  ```
125
137
 
126
- > **Use the `[plugin, options]` tuple, not `remarkAffiliate(affiliate)` pre-invoked.** Astro/unified
127
- > calls the plugin function itself with the options; passing an already-invoked transformer means
128
- > unified calls *that* with no arguments as if it were the attacher, which silently no-ops instead
129
- > of rewriting anything - the build stays green with `affiliate:key` links left untouched in the
130
- > output. Always verify by checking rendered HTML for the real resolved URL, not just a passing
131
- > build.
138
+ On Astro 6.0 to 6.3, which predate `markdown.processor` and bundle `@astrojs/markdown-remark`
139
+ themselves, use the top-level option instead: `markdown: { remarkPlugins: [[remarkAffiliate, affiliate]] }`.
140
+ Astro 6.4+ still accepts that form but deprecates it, and Astro 7 fails at config load with it
141
+ unless `@astrojs/markdown-remark` is installed.
142
+
143
+ > **Use the `[plugin, options]` tuple, not `remarkAffiliate(affiliate)` pre-invoked.** unified
144
+ > calls the plugin function itself with the options. Passing an already-invoked transformer means
145
+ > unified calls *that* as the plugin, with no document, and the build fails with
146
+ > `remarkAffiliate was passed pre-invoked`. Always verify by checking rendered HTML for the real
147
+ > resolved URL, not just a passing build.
148
+
149
+ Components-only consumers (`<AffiliateLink>`, `<AffiliateDisclosure>`) don't need
150
+ `@astrojs/markdown-remark`; it's an optional peer dependency.
132
151
 
133
152
  Then in a post's markdown body:
134
153
 
@@ -204,7 +223,8 @@ const { affiliates = [] } = entry.data;
204
223
  ```
205
224
 
206
225
  Renders one paragraph joining the disclosure text for every program in `affiliates`, or nothing
207
- if the array is empty. Default styling is `text-sm text-muted italic`; pass `class` to override,
226
+ if the array is empty. A program named more than once (for example, one entry per card in a
227
+ card list) is disclosed once. Default styling is `text-sm text-muted italic`; pass `class` to override,
208
228
  see [Per-app glue](#per-app-glue) for the token variables this assumes.
209
229
 
210
230
  ### Localized disclosure text
@@ -244,7 +264,8 @@ import { affiliate } from '../config/affiliate';
244
264
  const disclosures = resolveDisclosures(affiliate, entry.data.affiliates ?? [], locale);
245
265
  ```
246
266
 
247
- It returns the resolved text for each program name in order (see [Localized disclosure
267
+ It returns the resolved text for each distinct program name, in first-seen order - a name passed
268
+ more than once appears once (see [Localized disclosure
248
269
  text](#localized-disclosure-text) for how `locale` selects between entries), and throws if any
249
270
  name isn't a program declared in `config.programs` - the same guarantee `<AffiliateDisclosure>`
250
271
  relies on internally, so a plain-text consumer can't silently drop a disclosure for a typo'd
@@ -323,7 +344,8 @@ Publishing / OIDC, no token secret):
323
344
 
324
345
  1. Test before tagging: `npm pack`, install the tarball into a scratch Astro app (or a consumer
325
346
  locally), `astro check && astro build`.
326
- 2. Bump `version` in `package.json`, commit.
347
+ 2. Bump the version with `npm version X.Y.Z --no-git-tag-version`, which updates `package.json`
348
+ and `package-lock.json` together, then commit both.
327
349
  3. Tag `vX.Y.Z` and push the tag. Pushing the tag runs `.github/workflows/publish.yml`, which
328
350
  type-checks, tests, verifies the tag matches `package.json`'s version, and only then runs
329
351
  `npm publish`.
@@ -336,6 +358,8 @@ Publishing / OIDC, no token secret):
336
358
  - [imperfectsystems.com](https://imperfectsystems.com)
337
359
  - [freetoolbox.net](https://freetoolbox.net)
338
360
  - [vicstradamus.com](https://vicstradamus.com)
361
+ - [deepcutatlas.com](https://deepcutatlas.com)
362
+ - [roomforstars.com](https://roomforstars.com)
339
363
 
340
364
  ## License
341
365
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vdaluz/astro-affiliate",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "FTC-compliant affiliate-link catalog resolver and disclosure components - proven in production on vdaluz.com and imperfectsystems.com.",
5
5
  "keywords": [
6
6
  "astro",
@@ -37,10 +37,17 @@
37
37
  "./AffiliateDisclosure.astro": "./src/components/AffiliateDisclosure.astro"
38
38
  },
39
39
  "peerDependencies": {
40
+ "@astrojs/markdown-remark": ">=7.0.0",
40
41
  "astro": ">=6.0.0"
41
42
  },
43
+ "peerDependenciesMeta": {
44
+ "@astrojs/markdown-remark": {
45
+ "optional": true
46
+ }
47
+ },
42
48
  "devDependencies": {
43
49
  "@astrojs/check": "^0.9.10",
50
+ "@astrojs/markdown-remark": "^7.3.1",
44
51
  "@types/node": "^26.1.1",
45
52
  "astro": "^7.3.2",
46
53
  "typescript": "^6.0.3"
@@ -1,6 +1,6 @@
1
1
  ---
2
- import type { AffiliateConfig } from '../lib/types';
3
- import { resolveDisclosures } from '../lib/disclosures';
2
+ import type { AffiliateConfig } from '../lib/types.ts';
3
+ import { resolveDisclosures } from '../lib/disclosures.ts';
4
4
 
5
5
  interface Props {
6
6
  config: AffiliateConfig;
@@ -1,6 +1,6 @@
1
1
  ---
2
- import type { AffiliateConfig } from '../lib/types';
3
- import { resolveAffiliate } from '../lib/resolve';
2
+ import type { AffiliateConfig } from '../lib/types.ts';
3
+ import { resolveAffiliate } from '../lib/resolve.ts';
4
4
 
5
5
  interface Props {
6
6
  config: AffiliateConfig;
@@ -5,10 +5,11 @@ import { resolveLocalized } from './i18n.ts';
5
5
  * Resolves a post's `affiliates:` frontmatter names to their disclosure text.
6
6
  * Throws on an unknown program name instead of silently omitting its
7
7
  * disclosure - a typo'd/renamed program must fail the build, not ship a
8
- * page that uses the program without disclosing it.
8
+ * page that uses the program without disclosing it. Repeated program names
9
+ * collapse to one entry, in first-seen order.
9
10
  */
10
11
  export function resolveDisclosures(config: AffiliateConfig, affiliates: string[], locale?: string): string[] {
11
- return affiliates.map((name) => {
12
+ return [...new Set(affiliates)].map((name) => {
12
13
  const program = config.programs[name];
13
14
  if (!program) {
14
15
  throw new Error(
package/src/lib/remark.ts CHANGED
@@ -68,14 +68,21 @@ function assertNoRemainingAffiliateUrls(node: MdastNode) {
68
68
  * For a reference-style link (`[text][ref]`), "document order" follows the
69
69
  * position of the `[ref]: affiliate:key` definition, not the in-prose usage.
70
70
  *
71
+ * import { unified } from '@astrojs/markdown-remark';
71
72
  * import { remarkAffiliate } from '@vdaluz/astro-affiliate/remark';
72
73
  *
73
74
  * export default defineConfig({
74
- * markdown: { remarkPlugins: [[remarkAffiliate, affiliate]] },
75
+ * markdown: { processor: unified({ remarkPlugins: [[remarkAffiliate, affiliate]] }) },
75
76
  * });
76
77
  */
77
78
  export function remarkAffiliate(config: AffiliateConfig) {
78
79
  return (tree: MdastNode, file: VFileWithAstroFrontmatter) => {
80
+ // unified calls a pre-invoked remarkAffiliate(config) as the plugin attacher, with no tree.
81
+ if (!tree) {
82
+ throw new Error(
83
+ 'remarkAffiliate was passed pre-invoked. Pass it as a [remarkAffiliate, config] tuple instead.'
84
+ );
85
+ }
79
86
  const linkNodes: MdastNode[] = [];
80
87
  collectAffiliateLinkNodes(tree, linkNodes);
81
88