@dr-ishaan/rehype-perfect-code-blocks 1.1.4 → 1.1.6

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.
@@ -1 +1 @@
1
- {"version":3,"file":"astro.d.ts","sourceRoot":"","sources":["../src/astro.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAI9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAmBrD,MAAM,CAAC,OAAO,UAAU,WAAW,CACjC,OAAO,GAAE,kBAAuB,GAC/B,gBAAgB,CAqElB"}
1
+ {"version":3,"file":"astro.d.ts","sourceRoot":"","sources":["../src/astro.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAI9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAuCrD,MAAM,CAAC,OAAO,UAAU,WAAW,CACjC,OAAO,GAAE,kBAAuB,GAC/B,gBAAgB,CAwFlB"}
package/dist/astro.js CHANGED
@@ -20,6 +20,7 @@ import { rehypePerfectCodeBlocks } from './index.js';
20
20
  import { remarkPreserveCodeMeta } from './remark.js';
21
21
  import { COPY_SCRIPT } from './copy-script.js';
22
22
  import { readFileSync } from 'node:fs';
23
+ import { readdirSync, writeFileSync, readFileSync as readFile } from 'node:fs';
23
24
  import { fileURLToPath } from 'node:url';
24
25
  import { dirname, join } from 'node:path';
25
26
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -36,11 +37,31 @@ function loadCss() {
36
37
  }
37
38
  }
38
39
  }
40
+ /** Recursively walk a directory and return all .html file paths. */
41
+ function findHtmlFiles(dir) {
42
+ const results = [];
43
+ try {
44
+ const entries = readdirSync(dir, { withFileTypes: true });
45
+ for (const entry of entries) {
46
+ const fullPath = join(dir, entry.name);
47
+ if (entry.isDirectory()) {
48
+ results.push(...findHtmlFiles(fullPath));
49
+ }
50
+ else if (entry.name.endsWith('.html')) {
51
+ results.push(fullPath);
52
+ }
53
+ }
54
+ }
55
+ catch {
56
+ // Directory doesn't exist or can't be read
57
+ }
58
+ return results;
59
+ }
39
60
  export default function perfectCode(options = {}) {
40
61
  return {
41
62
  name: 'rehype-perfect-code-blocks',
42
63
  hooks: {
43
- 'astro:config:setup': ({ updateConfig, injectScript }) => {
64
+ 'astro:config:setup': ({ updateConfig }) => {
44
65
  // 1. Register the remark + rehype plugins with the Markdown pipeline.
45
66
  const userRehypePlugins = options.rehypePlugins ?? [];
46
67
  updateConfig({
@@ -57,38 +78,59 @@ export default function perfectCode(options = {}) {
57
78
  [rehypePerfectCodeBlocks, options],
58
79
  ],
59
80
  },
60
- // 2. Inject CSS via a Vite plugin (not injectScript, which is JS-only
61
- // in Astro v5+). The transformIndexHtml hook inserts a <style>
62
- // tag into every page's <head>.
63
- vite: {
64
- plugins: [
65
- {
66
- name: 'rehype-perfect-code-blocks-css',
67
- transformIndexHtml(html) {
68
- if (options.injectStyles === false)
69
- return html;
70
- const css = loadCss();
71
- if (!css)
72
- return html;
73
- return html.replace('</head>', `<style data-pcb>${css}</style></head>`);
74
- },
75
- },
76
- ],
77
- },
78
81
  });
79
- // 3. Inject the copy-button script once per page.
82
+ },
83
+ 'astro:build:done': ({ dir }) => {
84
+ // 2. After Astro finishes building all pages, inject CSS + scripts
85
+ // into every generated .html file. This is the most reliable
86
+ // method — it works with Astro v4, v5, static and SSR modes,
87
+ // and doesn't require the user to import anything in their layout.
88
+ const injections = [];
89
+ // CSS
90
+ if (options.injectStyles !== false) {
91
+ const css = loadCss();
92
+ if (css) {
93
+ injections.push(`<style data-pcb>${css}</style>`);
94
+ }
95
+ }
96
+ // Copy-button script
80
97
  if (options.copyButton !== false) {
81
- injectScript('page', `<script>${COPY_SCRIPT}</script>`);
82
- // 3a. Graceful degradation: add .no-js to <html>.
98
+ injections.push(`<script>${COPY_SCRIPT}</script>`);
99
+ // Graceful degradation: .no-js class
83
100
  if (options.hideCopyWithoutJs !== false) {
84
- injectScript('page', `<script>document.documentElement.classList.add('no-js');</script>`);
101
+ injections.push(`<script>document.documentElement.classList.add('no-js');</script>`);
85
102
  }
86
103
  }
87
- // 4. Respect manual theme override.
104
+ // Manual theme override
88
105
  if (options.theme && options.theme !== 'auto') {
89
106
  const safeTheme = ['dark', 'light'].includes(options.theme) ? options.theme : 'auto';
90
107
  if (safeTheme !== 'auto') {
91
- injectScript('page', `<script>document.documentElement.setAttribute('data-theme','${safeTheme}');</script>`);
108
+ injections.push(`<script>document.documentElement.setAttribute('data-theme','${safeTheme}');</script>`);
109
+ }
110
+ }
111
+ if (injections.length === 0)
112
+ return;
113
+ const injectionHtml = injections.join('\n');
114
+ const outputDir = fileURLToPath(dir);
115
+ const htmlFiles = findHtmlFiles(outputDir);
116
+ for (const htmlFile of htmlFiles) {
117
+ try {
118
+ const html = readFile(htmlFile, 'utf8');
119
+ // Inject before </head>. If no </head> (rare), inject after <html...>.
120
+ let updated;
121
+ if (html.includes('</head>')) {
122
+ updated = html.replace('</head>', `${injectionHtml}</head>`);
123
+ }
124
+ else if (html.includes('<body')) {
125
+ updated = html.replace('<body', `${injectionHtml}<body`);
126
+ }
127
+ else {
128
+ updated = injectionHtml + html;
129
+ }
130
+ writeFileSync(htmlFile, updated);
131
+ }
132
+ catch {
133
+ // Skip files that can't be read/written
92
134
  }
93
135
  }
94
136
  },
package/dist/astro.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"astro.js","sourceRoot":"","sources":["../src/astro.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"astro.js","sourceRoot":"","sources":["../src/astro.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dr-ishaan/rehype-perfect-code-blocks",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Beautiful, configurable code blocks for Astro / MDX / any rehype pipeline. Built on Shiki, inspired by rehype-pretty-code, VitePress, Docusaurus, and Expressive Code.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/astro.ts CHANGED
@@ -23,6 +23,7 @@ import { remarkPreserveCodeMeta } from './remark.js';
23
23
  import { COPY_SCRIPT } from './copy-script.js';
24
24
  import type { PerfectCodeOptions } from './types.js';
25
25
  import { readFileSync } from 'node:fs';
26
+ import { readdirSync, writeFileSync, readFileSync as readFile } from 'node:fs';
26
27
  import { fileURLToPath } from 'node:url';
27
28
  import { dirname, join } from 'node:path';
28
29
 
@@ -40,13 +41,32 @@ function loadCss(): string {
40
41
  }
41
42
  }
42
43
 
44
+ /** Recursively walk a directory and return all .html file paths. */
45
+ function findHtmlFiles(dir: string): string[] {
46
+ const results: string[] = [];
47
+ try {
48
+ const entries = readdirSync(dir, { withFileTypes: true });
49
+ for (const entry of entries) {
50
+ const fullPath = join(dir, entry.name);
51
+ if (entry.isDirectory()) {
52
+ results.push(...findHtmlFiles(fullPath));
53
+ } else if (entry.name.endsWith('.html')) {
54
+ results.push(fullPath);
55
+ }
56
+ }
57
+ } catch {
58
+ // Directory doesn't exist or can't be read
59
+ }
60
+ return results;
61
+ }
62
+
43
63
  export default function perfectCode(
44
64
  options: PerfectCodeOptions = {}
45
65
  ): AstroIntegration {
46
66
  return {
47
67
  name: 'rehype-perfect-code-blocks',
48
68
  hooks: {
49
- 'astro:config:setup': ({ updateConfig, injectScript }) => {
69
+ 'astro:config:setup': ({ updateConfig }) => {
50
70
  // 1. Register the remark + rehype plugins with the Markdown pipeline.
51
71
  const userRehypePlugins = options.rehypePlugins ?? [];
52
72
  updateConfig({
@@ -64,50 +84,69 @@ export default function perfectCode(
64
84
  [rehypePerfectCodeBlocks, options],
65
85
  ] as never,
66
86
  },
67
- // 2. Inject CSS via a Vite plugin (not injectScript, which is JS-only
68
- // in Astro v5+). The transformIndexHtml hook inserts a <style>
69
- // tag into every page's <head>.
70
- vite: {
71
- plugins: [
72
- {
73
- name: 'rehype-perfect-code-blocks-css',
74
- transformIndexHtml(html: string) {
75
- if (options.injectStyles === false) return html;
76
- const css = loadCss();
77
- if (!css) return html;
78
- return html.replace(
79
- '</head>',
80
- `<style data-pcb>${css}</style></head>`
81
- );
82
- },
83
- },
84
- ],
85
- },
86
87
  });
88
+ },
89
+
90
+ 'astro:build:done': ({ dir }) => {
91
+ // 2. After Astro finishes building all pages, inject CSS + scripts
92
+ // into every generated .html file. This is the most reliable
93
+ // method — it works with Astro v4, v5, static and SSR modes,
94
+ // and doesn't require the user to import anything in their layout.
95
+ const injections: string[] = [];
87
96
 
88
- // 3. Inject the copy-button script once per page.
97
+ // CSS
98
+ if (options.injectStyles !== false) {
99
+ const css = loadCss();
100
+ if (css) {
101
+ injections.push(`<style data-pcb>${css}</style>`);
102
+ }
103
+ }
104
+
105
+ // Copy-button script
89
106
  if (options.copyButton !== false) {
90
- injectScript('page', `<script>${COPY_SCRIPT}</script>`);
107
+ injections.push(`<script>${COPY_SCRIPT}</script>`);
91
108
 
92
- // 3a. Graceful degradation: add .no-js to <html>.
109
+ // Graceful degradation: .no-js class
93
110
  if (options.hideCopyWithoutJs !== false) {
94
- injectScript(
95
- 'page',
111
+ injections.push(
96
112
  `<script>document.documentElement.classList.add('no-js');</script>`
97
113
  );
98
114
  }
99
115
  }
100
116
 
101
- // 4. Respect manual theme override.
117
+ // Manual theme override
102
118
  if (options.theme && options.theme !== 'auto') {
103
119
  const safeTheme = ['dark', 'light'].includes(options.theme) ? options.theme : 'auto';
104
120
  if (safeTheme !== 'auto') {
105
- injectScript(
106
- 'page',
121
+ injections.push(
107
122
  `<script>document.documentElement.setAttribute('data-theme','${safeTheme}');</script>`
108
123
  );
109
124
  }
110
125
  }
126
+
127
+ if (injections.length === 0) return;
128
+
129
+ const injectionHtml = injections.join('\n');
130
+ const outputDir = fileURLToPath(dir);
131
+ const htmlFiles = findHtmlFiles(outputDir);
132
+
133
+ for (const htmlFile of htmlFiles) {
134
+ try {
135
+ const html = readFile(htmlFile, 'utf8');
136
+ // Inject before </head>. If no </head> (rare), inject after <html...>.
137
+ let updated: string;
138
+ if (html.includes('</head>')) {
139
+ updated = html.replace('</head>', `${injectionHtml}</head>`);
140
+ } else if (html.includes('<body')) {
141
+ updated = html.replace('<body', `${injectionHtml}<body`);
142
+ } else {
143
+ updated = injectionHtml + html;
144
+ }
145
+ writeFileSync(htmlFile, updated);
146
+ } catch {
147
+ // Skip files that can't be read/written
148
+ }
149
+ }
111
150
  },
112
151
  },
113
152
  };