@levino/shipyard-base 0.8.5 → 0.9.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
@@ -10,10 +10,10 @@ npm install @levino/shipyard-base
10
10
 
11
11
  ## Peer Dependencies
12
12
 
13
- - `astro` ^5.7
14
- - `tailwindcss` ^3
15
- - `daisyui` ^4
16
- - `@tailwindcss/typography` ^0.5.10
13
+ - `astro` ^7.2.4
14
+ - `tailwindcss` ^4
15
+ - `daisyui` ^5
16
+ - `@tailwindcss/typography` ^0.5.20
17
17
 
18
18
  ## Basic Usage
19
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@levino/shipyard-base",
3
- "version": "0.8.5",
3
+ "version": "0.9.0",
4
4
  "description": "Core layouts, components, and configuration for shipyard - a composable page builder for Astro",
5
5
  "keywords": [
6
6
  "astro",
@@ -38,8 +38,8 @@
38
38
  "./styles/components.css": "./src/styles/components.css"
39
39
  },
40
40
  "peerDependencies": {
41
- "@tailwindcss/typography": "^0.5.10",
42
- "astro": "^6.1.6",
41
+ "@tailwindcss/typography": "^0.5.20",
42
+ "astro": "^7.2.4",
43
43
  "daisyui": "^5",
44
44
  "tailwindcss": "^4"
45
45
  },
@@ -53,21 +53,22 @@
53
53
  "url": "https://github.com/levino/shipyard"
54
54
  },
55
55
  "devDependencies": {
56
- "@tailwindcss/typography": "^0.5.10",
57
- "@types/ramda": "^0.31",
56
+ "@tailwindcss/typography": "^0.5.20",
57
+ "@types/ramda": "^0.32",
58
58
  "daisyui": "^5",
59
59
  "remark": "^15.0.1",
60
60
  "remark-html": "^16.0.1",
61
61
  "tailwindcss": "^4",
62
- "typescript": "^5.2.2",
63
- "vite": "^7.3.2",
64
- "vitest": "^4.1.0"
62
+ "typescript": "^7.0.2",
63
+ "vite": "^8.2.2",
64
+ "vitest": "^4.1.11"
65
65
  },
66
66
  "dependencies": {
67
+ "@astrojs/markdown-remark": "^7.2.4",
67
68
  "clsx": "^2.1.0",
68
69
  "mdast-util-directive": "^3.0.0",
69
70
  "micromark-extension-directive": "^3.0.0",
70
- "ramda": "^0.31",
71
+ "ramda": "^0.32.0",
71
72
  "remark-directive": "^3.0.0",
72
73
  "tailwind-merge": "^2.2.0",
73
74
  "unified": "^11.0.0",
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { fileURLToPath } from 'node:url'
2
+ import { isUnifiedProcessor, unified } from '@astrojs/markdown-remark'
2
3
  import type { AstroIntegration } from 'astro'
3
4
  import { remarkAdmonitions } from './remark/remarkAdmonitions'
4
5
  import { remarkBlockDirective } from './remark/remarkBlockDirective'
@@ -28,7 +29,7 @@ export default (config: Config): AstroIntegration => {
28
29
  return {
29
30
  name: 'shipyard',
30
31
  hooks: {
31
- 'astro:config:setup': ({ updateConfig, config: astroConfig }) => {
32
+ 'astro:config:setup': ({ updateConfig, config: astroConfig, logger }) => {
32
33
  // Detect server mode (output: 'server' or 'hybrid')
33
34
  isServerMode = astroConfig.output === 'server'
34
35
 
@@ -45,13 +46,37 @@ export default (config: Config): AstroIntegration => {
45
46
  [shipyardCssId]: config.css ? `import '${config.css}';` : '',
46
47
  } as Record<string, string | undefined>
47
48
 
49
+ // Astro 7 renders Markdown with Sätteri by default, which does not run
50
+ // unified plugins. shipyard's admonitions, npm2yarn tabs and block
51
+ // directives are all remark plugins, so we pin the unified processor
52
+ // from @astrojs/markdown-remark. Any plugins the user already put on
53
+ // their own `unified()` processor are carried over so both sets run;
54
+ // plugins they pass via the deprecated `markdown.remarkPlugins` option
55
+ // are appended by Astro afterwards.
56
+ const userProcessor = astroConfig.markdown?.processor
57
+ const inherited =
58
+ userProcessor && isUnifiedProcessor(userProcessor)
59
+ ? userProcessor.options
60
+ : undefined
61
+
62
+ if (userProcessor && !inherited) {
63
+ logger.warn(
64
+ `\`markdown.processor\` is set to \`${userProcessor.name}\`, which does not run remark plugins. ` +
65
+ 'Overriding it with `unified()` so shipyard admonitions, npm2yarn tabs and block directives keep working.',
66
+ )
67
+ }
68
+
48
69
  updateConfig({
49
70
  markdown: {
50
- remarkPlugins: [
51
- remarkBlockDirective,
52
- remarkAdmonitions,
53
- remarkNpm2Yarn,
54
- ],
71
+ processor: unified({
72
+ ...inherited,
73
+ remarkPlugins: [
74
+ ...(inherited?.remarkPlugins ?? []),
75
+ remarkBlockDirective,
76
+ remarkAdmonitions,
77
+ remarkNpm2Yarn,
78
+ ],
79
+ }),
55
80
  },
56
81
  vite: {
57
82
  plugins: [
@@ -0,0 +1,92 @@
1
+ import type { UnifiedResolvedOptions } from '@astrojs/markdown-remark'
2
+ import { unified } from '@astrojs/markdown-remark'
3
+ import { describe, expect, test, vi } from 'vitest'
4
+ import shipyard from './index'
5
+ import type { Config } from './schemas/config'
6
+
7
+ const SHIPYARD_PLUGIN_COUNT = 3
8
+
9
+ interface ConfigUpdate {
10
+ markdown?: {
11
+ remarkPlugins?: unknown
12
+ processor?: { name: string; options: UnifiedResolvedOptions }
13
+ }
14
+ }
15
+
16
+ const baseConfig: Config = {
17
+ title: 'Test',
18
+ brand: 'Test',
19
+ navigation: {},
20
+ }
21
+
22
+ /**
23
+ * Runs the integration's `astro:config:setup` hook against a fake Astro config
24
+ * and returns whatever it passed to `updateConfig`, plus the logger it used.
25
+ */
26
+ const runConfigSetup = (astroConfig: Record<string, unknown> = {}) => {
27
+ const updates: ConfigUpdate[] = []
28
+ const logger = { warn: vi.fn(), info: vi.fn(), error: vi.fn() }
29
+
30
+ const integration = shipyard(baseConfig)
31
+ const hook = integration.hooks['astro:config:setup']
32
+ if (!hook) throw new Error('astro:config:setup hook missing')
33
+
34
+ hook({
35
+ updateConfig: (update: ConfigUpdate) => updates.push(update),
36
+ config: astroConfig,
37
+ logger,
38
+ } as never)
39
+
40
+ return { update: updates[0], logger }
41
+ }
42
+
43
+ describe('markdown processor', () => {
44
+ test('pins the unified processor so remark plugins run under Astro 7', () => {
45
+ const { update } = runConfigSetup()
46
+
47
+ expect(update?.markdown?.processor?.name).toBe(unified().name)
48
+ expect(update?.markdown?.processor?.options.remarkPlugins).toHaveLength(
49
+ SHIPYARD_PLUGIN_COUNT,
50
+ )
51
+ })
52
+
53
+ test('does not use the deprecated markdown.remarkPlugins option', () => {
54
+ const { update } = runConfigSetup()
55
+
56
+ expect(update?.markdown).not.toHaveProperty('remarkPlugins')
57
+ })
58
+
59
+ test("keeps the user's own plugins on an existing unified processor", () => {
60
+ const userPlugin = () => undefined
61
+ const { update, logger } = runConfigSetup({
62
+ markdown: { processor: unified({ remarkPlugins: [userPlugin] }) },
63
+ })
64
+
65
+ const plugins = update?.markdown?.processor?.options.remarkPlugins
66
+ expect(plugins).toHaveLength(SHIPYARD_PLUGIN_COUNT + 1)
67
+ expect(plugins?.[0]).toBe(userPlugin)
68
+ expect(logger.warn).not.toHaveBeenCalled()
69
+ })
70
+
71
+ test("carries over the user's other unified options", () => {
72
+ const { update } = runConfigSetup({
73
+ markdown: { processor: unified({ gfm: false, smartypants: false }) },
74
+ })
75
+
76
+ expect(update?.markdown?.processor?.options.gfm).toBe(false)
77
+ expect(update?.markdown?.processor?.options.smartypants).toBe(false)
78
+ })
79
+
80
+ test('warns and overrides when a non-unified processor is configured', () => {
81
+ const { update, logger } = runConfigSetup({
82
+ markdown: { processor: { name: 'satteri', options: {} } },
83
+ })
84
+
85
+ expect(logger.warn).toHaveBeenCalledOnce()
86
+ expect(logger.warn.mock.calls[0][0]).toContain('satteri')
87
+ expect(update?.markdown?.processor?.name).toBe(unified().name)
88
+ expect(update?.markdown?.processor?.options.remarkPlugins).toHaveLength(
89
+ SHIPYARD_PLUGIN_COUNT,
90
+ )
91
+ })
92
+ })