@dr-ishaan/rehype-perfect-code-blocks 1.1.5 → 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,CA0ElB"}
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,6 +37,26 @@ 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',
@@ -57,48 +78,62 @@ export default function perfectCode(options = {}) {
57
78
  [rehypePerfectCodeBlocks, options],
58
79
  ],
59
80
  },
60
- // 2. Inject CSS + scripts via Vite's transformIndexHtml hook.
61
- // We can't use Astro's injectScript() because Astro v5 tries to
62
- // parse/execute the injected JS during build (SSR), which fails
63
- // because `document` isn't available. transformIndexHtml just
64
- // inserts raw HTML into <head>.
65
- vite: {
66
- plugins: [
67
- {
68
- name: 'rehype-perfect-code-blocks-inject',
69
- transformIndexHtml(html) {
70
- const injections = [];
71
- // CSS
72
- if (options.injectStyles !== false) {
73
- const css = loadCss();
74
- if (css) {
75
- injections.push(`<style data-pcb>${css}</style>`);
76
- }
77
- }
78
- // Copy-button script
79
- if (options.copyButton !== false) {
80
- injections.push(`<script>${COPY_SCRIPT}</script>`);
81
- // Graceful degradation: .no-js class
82
- if (options.hideCopyWithoutJs !== false) {
83
- injections.push(`<script>document.documentElement.classList.add('no-js');</script>`);
84
- }
85
- }
86
- // Manual theme override
87
- if (options.theme && options.theme !== 'auto') {
88
- const safeTheme = ['dark', 'light'].includes(options.theme) ? options.theme : 'auto';
89
- if (safeTheme !== 'auto') {
90
- injections.push(`<script>document.documentElement.setAttribute('data-theme','${safeTheme}');</script>`);
91
- }
92
- }
93
- if (injections.length === 0)
94
- return html;
95
- return html.replace('</head>', `${injections.join('\n')}</head>`);
96
- },
97
- },
98
- ],
99
- },
100
81
  });
101
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
97
+ if (options.copyButton !== false) {
98
+ injections.push(`<script>${COPY_SCRIPT}</script>`);
99
+ // Graceful degradation: .no-js class
100
+ if (options.hideCopyWithoutJs !== false) {
101
+ injections.push(`<script>document.documentElement.classList.add('no-js');</script>`);
102
+ }
103
+ }
104
+ // Manual theme override
105
+ if (options.theme && options.theme !== 'auto') {
106
+ const safeTheme = ['dark', 'light'].includes(options.theme) ? options.theme : 'auto';
107
+ if (safeTheme !== 'auto') {
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
134
+ }
135
+ }
136
+ },
102
137
  },
103
138
  };
104
139
  }
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.5",
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,6 +41,25 @@ 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 {
@@ -64,55 +84,69 @@ export default function perfectCode(
64
84
  [rehypePerfectCodeBlocks, options],
65
85
  ] as never,
66
86
  },
67
- // 2. Inject CSS + scripts via Vite's transformIndexHtml hook.
68
- // We can't use Astro's injectScript() because Astro v5 tries to
69
- // parse/execute the injected JS during build (SSR), which fails
70
- // because `document` isn't available. transformIndexHtml just
71
- // inserts raw HTML into <head>.
72
- vite: {
73
- plugins: [
74
- {
75
- name: 'rehype-perfect-code-blocks-inject',
76
- transformIndexHtml(html: string) {
77
- const injections: string[] = [];
87
+ });
88
+ },
78
89
 
79
- // CSS
80
- if (options.injectStyles !== false) {
81
- const css = loadCss();
82
- if (css) {
83
- injections.push(`<style data-pcb>${css}</style>`);
84
- }
85
- }
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[] = [];
86
96
 
87
- // Copy-button script
88
- if (options.copyButton !== false) {
89
- injections.push(`<script>${COPY_SCRIPT}</script>`);
97
+ // CSS
98
+ if (options.injectStyles !== false) {
99
+ const css = loadCss();
100
+ if (css) {
101
+ injections.push(`<style data-pcb>${css}</style>`);
102
+ }
103
+ }
90
104
 
91
- // Graceful degradation: .no-js class
92
- if (options.hideCopyWithoutJs !== false) {
93
- injections.push(
94
- `<script>document.documentElement.classList.add('no-js');</script>`
95
- );
96
- }
97
- }
105
+ // Copy-button script
106
+ if (options.copyButton !== false) {
107
+ injections.push(`<script>${COPY_SCRIPT}</script>`);
98
108
 
99
- // Manual theme override
100
- if (options.theme && options.theme !== 'auto') {
101
- const safeTheme = ['dark', 'light'].includes(options.theme) ? options.theme : 'auto';
102
- if (safeTheme !== 'auto') {
103
- injections.push(
104
- `<script>document.documentElement.setAttribute('data-theme','${safeTheme}');</script>`
105
- );
106
- }
107
- }
109
+ // Graceful degradation: .no-js class
110
+ if (options.hideCopyWithoutJs !== false) {
111
+ injections.push(
112
+ `<script>document.documentElement.classList.add('no-js');</script>`
113
+ );
114
+ }
115
+ }
108
116
 
109
- if (injections.length === 0) return html;
110
- return html.replace('</head>', `${injections.join('\n')}</head>`);
111
- },
112
- },
113
- ],
114
- },
115
- });
117
+ // Manual theme override
118
+ if (options.theme && options.theme !== 'auto') {
119
+ const safeTheme = ['dark', 'light'].includes(options.theme) ? options.theme : 'auto';
120
+ if (safeTheme !== 'auto') {
121
+ injections.push(
122
+ `<script>document.documentElement.setAttribute('data-theme','${safeTheme}');</script>`
123
+ );
124
+ }
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
+ }
116
150
  },
117
151
  },
118
152
  };