diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 86f9a3f0..fed4ef6d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -62,7 +62,7 @@ updates: - 'recma-*' compiling: patterns: - - '@swc/html' + - '@minify-html/wasm' - '@rollup/*' - 'rolldown' - 'lightningcss' diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 4dc455ec..13e7e66a 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -8,12 +8,12 @@ "dependencies": { "@actions/core": "^3.0.0", "@heroicons/react": "^2.2.0", + "@minify-html/wasm": "^0.18.1", "@node-core/rehype-shiki": "^1.4.0", "@node-core/ui-components": "^1.6.0", "@orama/orama": "^3.1.18", "@orama/ui": "^1.5.4", "@rollup/plugin-virtual": "^3.0.2", - "@swc/html": "^1.15.11", "acorn": "^8.15.0", "commander": "^14.0.2", "dedent": "^1.7.1", @@ -637,6 +637,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@minify-html/wasm": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@minify-html/wasm/-/wasm-0.18.1.tgz", + "integrity": "sha512-GBkBOJxe7duO+z2b00SP83EewOI+Qm4MsnajXHw4yT7/J+TuG3jLEatBHKnT59Zq4CgXBRpdkv/2hlCGnyqAzg==", + "license": "MIT" + }, "node_modules/@napi-rs/nice": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz", @@ -2429,196 +2435,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "license": "Apache-2.0" - }, - "node_modules/@swc/html": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/html/-/html-1.15.11.tgz", - "integrity": "sha512-JOO48SCxyR3KbH9iiLMSWRDm7+Xl6lgvOBe5di54W8PsumdeP0Imatq8cunkRAUR7br8xAjlpB16bX6iJeyeMw==", - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3" - }, - "engines": { - "node": ">=14" - }, - "optionalDependencies": { - "@swc/html-darwin-arm64": "1.15.11", - "@swc/html-darwin-x64": "1.15.11", - "@swc/html-linux-arm-gnueabihf": "1.15.11", - "@swc/html-linux-arm64-gnu": "1.15.11", - "@swc/html-linux-arm64-musl": "1.15.11", - "@swc/html-linux-x64-gnu": "1.15.11", - "@swc/html-linux-x64-musl": "1.15.11", - "@swc/html-win32-arm64-msvc": "1.15.11", - "@swc/html-win32-ia32-msvc": "1.15.11", - "@swc/html-win32-x64-msvc": "1.15.11" - } - }, - "node_modules/@swc/html-darwin-arm64": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/html-darwin-arm64/-/html-darwin-arm64-1.15.11.tgz", - "integrity": "sha512-FTa0ypbMbUXiwXIIT76HMFQYewEdTsPKOQGDqI6/kUTzU+lCIqIJequdoh6gqQvMq2BtBu/15dkTYYIlp4JoJw==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/html-darwin-x64": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/html-darwin-x64/-/html-darwin-x64-1.15.11.tgz", - "integrity": "sha512-U+WFUFqbqS2WsLVCt40MuBGJ7mxDJi/T8RocBvQkoRwU3juOmeM2V/Od/QLBZxTFZTLQ3RCHJTkLXSDnxUWUuw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/html-linux-arm-gnueabihf": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/html-linux-arm-gnueabihf/-/html-linux-arm-gnueabihf-1.15.11.tgz", - "integrity": "sha512-Cid64465sEkAiTh8KLFCbQbsIbgDzPeGdA5ZYB8w5AXmYAGPJ0xbeRHEFq3iq469Dj2rxYvExrLCXNhn8rHbww==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/html-linux-arm64-gnu": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/html-linux-arm64-gnu/-/html-linux-arm64-gnu-1.15.11.tgz", - "integrity": "sha512-sxKdt0QJIR2x/6pAmDPK0tjfGVwsSZsfeY1BM8ODTm7h18ApE7KVNN+ZtsDbl4n5ejfXG7fOWNmMPUvj8qSMSA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/html-linux-arm64-musl": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/html-linux-arm64-musl/-/html-linux-arm64-musl-1.15.11.tgz", - "integrity": "sha512-aNACh2/HPy52VbKPqHieVRDeKzkO66DQdlhiVUi+fggdn8khvllni6Xr52INeAMjYFASrTTth/3vKXMv215t3A==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/html-linux-x64-gnu": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/html-linux-x64-gnu/-/html-linux-x64-gnu-1.15.11.tgz", - "integrity": "sha512-8YOar0XeRLBzA+UMMW5smGpsQamoZLtaQ5RKGfap21FxOUUXqkPhkDTRr+kBVCYb47yz3NokjTPaDGTWOYNyww==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/html-linux-x64-musl": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/html-linux-x64-musl/-/html-linux-x64-musl-1.15.11.tgz", - "integrity": "sha512-em2Ur0uGFA/nw2JbMclXu9mLuUC7q/1J06i8FZTRHqZzNGt9Q0UMdgH9T8HkGLT5e7dZ6ROJoq1H4st6B8N3uw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/html-win32-arm64-msvc": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/html-win32-arm64-msvc/-/html-win32-arm64-msvc-1.15.11.tgz", - "integrity": "sha512-Xf9Vd4UsYTs4ejBwS+j9zShkyp3KQ+qfn/ZKVMKDygWjuOjU6FFXWYm93/PdTmS5qD0c58FhmoqTv+uFEZ4nxQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/html-win32-ia32-msvc": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/html-win32-ia32-msvc/-/html-win32-ia32-msvc-1.15.11.tgz", - "integrity": "sha512-+YNzKR81UqH+xOZPU8XEIy5L6E63UUGSSEu1Wv3D85GVMQkZ8X9LZkZ5hq4vvqmCTV1zeHIPfzNrrVElqK198g==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/html-win32-x64-msvc": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/@swc/html-win32-x64-msvc/-/html-win32-x64-msvc-1.15.11.tgz", - "integrity": "sha512-Y3Xj62eA+pjadhgiVNFUBgIN9Wa1gYrB3N1l6NmSLeApVE2qkBww3WrVXhRDfqUXiWz7eRHPDakZSRQREyMv5A==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@tailwindcss/node": { "version": "4.1.18", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", diff --git a/package.json b/package.json index d81ffbc2..8d732d46 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,11 @@ "format": "prettier .", "format:write": "prettier --write .", "format:check": "prettier --check .", - "test": "node --test --experimental-test-module-mocks", - "test:coverage": "c8 npm test", - "test:ci": "c8 --reporter=lcov node --test --experimental-test-module-mocks --test-reporter=@reporters/github --test-reporter-destination=stdout --test-reporter=junit --test-reporter-destination=junit.xml --test-reporter=spec --test-reporter-destination=stdout", - "test:update-snapshots": "node --test --experimental-test-module-mocks --test-update-snapshots", - "test:watch": "node --test --experimental-test-module-mocks --watch", + "test": "node --test --experimental-test-module-mocks \"src/**/*.test.mjs\"", + "test:coverage": "c8 node --test --experimental-test-module-mocks \"src/**/*.test.mjs\"", + "test:ci": "c8 --reporter=lcov node --test --experimental-test-module-mocks \"src/**/*.test.mjs\" --test-reporter=@reporters/github --test-reporter-destination=stdout --test-reporter=junit --test-reporter-destination=junit.xml --test-reporter=spec --test-reporter-destination=stdout", + "test:update-snapshots": "node --test --experimental-test-module-mocks --test-update-snapshots \"src/**/*.test.mjs\"", + "test:watch": "node --test --experimental-test-module-mocks --watch \"src/**/*.test.mjs\"", "prepare": "husky || exit 0", "run": "node bin/cli.mjs", "watch": "node --watch bin/cli.mjs" @@ -42,12 +42,12 @@ "dependencies": { "@actions/core": "^3.0.0", "@heroicons/react": "^2.2.0", + "@minify-html/wasm": "^0.18.1", "@node-core/rehype-shiki": "^1.4.0", "@node-core/ui-components": "^1.6.0", "@orama/orama": "^3.1.18", "@orama/ui": "^1.5.4", "@rollup/plugin-virtual": "^3.0.2", - "@swc/html": "^1.15.11", "acorn": "^8.15.0", "commander": "^14.0.2", "dedent": "^1.7.1", diff --git a/src/generators/legacy-html-all/index.mjs b/src/generators/legacy-html-all/index.mjs index 051eead9..96e2808e 100644 --- a/src/generators/legacy-html-all/index.mjs +++ b/src/generators/legacy-html-all/index.mjs @@ -3,9 +3,8 @@ import { readFile, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; -import { minify } from '@swc/html'; - import getConfig from '../../utils/configuration/index.mjs'; +import { minifyHTML } from '../../utils/html-minifier.mjs'; import { getRemarkRehype } from '../../utils/remark.mjs'; import legacyHtml from '../legacy-html/index.mjs'; import { replaceTemplateValues } from '../legacy-html/utils/replaceTemplateValues.mjs'; @@ -86,7 +85,7 @@ export default { }); if (config.minify) { - ({ code: result } = await minify(result)); + result = Buffer.from(await minifyHTML(result)); } if (config.output) { diff --git a/src/generators/legacy-html/index.mjs b/src/generators/legacy-html/index.mjs index 8a33bccd..d647fe08 100644 --- a/src/generators/legacy-html/index.mjs +++ b/src/generators/legacy-html/index.mjs @@ -3,14 +3,13 @@ import { readFile, writeFile, mkdir } from 'node:fs/promises'; import { basename, join } from 'node:path'; -import { minify } from '@swc/html'; - import buildContent from './utils/buildContent.mjs'; import { replaceTemplateValues } from './utils/replaceTemplateValues.mjs'; import { safeCopy } from './utils/safeCopy.mjs'; import tableOfContents from './utils/tableOfContents.mjs'; import getConfig from '../../utils/configuration/index.mjs'; import { groupNodesByModule } from '../../utils/generators.mjs'; +import { minifyHTML } from '../../utils/html-minifier.mjs'; import { getRemarkRehypeWithShiki } from '../../utils/remark.mjs'; /** @@ -159,7 +158,7 @@ export default { let result = replaceTemplateValues(apiTemplate, template, config); if (config.minify) { - ({ code: result } = await minify(result)); + result = Buffer.from(await minifyHTML(result)); } await writeFile(join(config.output, `${template.api}.html`), result); diff --git a/src/generators/web/utils/processing.mjs b/src/generators/web/utils/processing.mjs index 5508edc5..e1c4d46a 100644 --- a/src/generators/web/utils/processing.mjs +++ b/src/generators/web/utils/processing.mjs @@ -1,12 +1,12 @@ import { randomUUID } from 'node:crypto'; -import { minifySync } from '@swc/html'; import { jsx, toJs } from 'estree-util-to-js'; import { transform } from 'lightningcss'; -import { SPECULATION_RULES } from '../constants.mjs'; import bundleCode from './bundle.mjs'; import { createChunkedRequire } from './chunks.mjs'; +import { minifyHTML } from '../../../utils/html-minifier.mjs'; +import { SPECULATION_RULES } from '../constants.mjs'; /** * Converts JSX AST entries to server and client JavaScript code. @@ -109,24 +109,26 @@ export async function processJSXEntries( const titleSuffix = `Node.js v${version.version} Documentation`; // Step 3: Create final HTML (could be parallelized in workers) - const results = entries.map(({ data: { api, heading } }) => { - const fileName = `${api}.js`; - const title = `${heading.data.name} | ${titleSuffix}`; - - // Replace template placeholders with actual content - const renderedHtml = template - .replace('{{title}}', title) - .replace('{{dehydrated}}', serverBundle.pages.get(fileName) ?? '') - .replace('{{importMap}}', clientBundle.importMap ?? '') - .replace('{{entrypoint}}', `./${fileName}?${randomUUID()}`) - .replace('{{speculationRules}}', SPECULATION_RULES) - .replace('{{ogTitle}}', title); - - // Minify HTML (input must be a Buffer) - const { code: html } = minifySync(renderedHtml); - - return { html, api }; - }); + const results = await Promise.all( + entries.map(async ({ data: { api, heading } }) => { + const fileName = `${api}.js`; + const title = `${heading.data.name} | ${titleSuffix}`; + + // Replace template placeholders with actual content + const renderedHtml = template + .replace('{{title}}', title) + .replace('{{dehydrated}}', serverBundle.pages.get(fileName) ?? '') + .replace('{{importMap}}', clientBundle.importMap ?? '') + .replace('{{entrypoint}}', `./${fileName}?${randomUUID()}`) + .replace('{{speculationRules}}', SPECULATION_RULES) + .replace('{{ogTitle}}', title); + + const minifiedHtml = await minifyHTML(renderedHtml); + const html = Buffer.from(minifiedHtml); + + return { html, api }; + }) + ); const { code: minifiedCSS } = transform({ code: Buffer.from(`${serverBundle.css}\n${clientBundle.css}`), diff --git a/src/utils/configuration/__tests__/index.test.mjs b/src/utils/configuration/__tests__/index.test.mjs index f6f3c43c..293a468f 100644 --- a/src/utils/configuration/__tests__/index.test.mjs +++ b/src/utils/configuration/__tests__/index.test.mjs @@ -137,8 +137,6 @@ describe('config.mjs', () => { threads: 2, }); - console.log(config); - assert.strictEqual(config.global.input, 'custom-src/'); assert.strictEqual(config.global.output, 'custom-dist/'); assert.strictEqual(config.threads, 2); diff --git a/src/utils/html-minifier.mjs b/src/utils/html-minifier.mjs new file mode 100644 index 00000000..e3921288 --- /dev/null +++ b/src/utils/html-minifier.mjs @@ -0,0 +1,24 @@ +import { minify } from '@minify-html/wasm'; + +const DEFAULT_HTML_MINIFIER_OPTIONS = { + minify_css: true, + minify_js: true, +}; + +const textEncoder = new TextEncoder(); +const textDecoder = new TextDecoder(); + +/** + * Minifies HTML with project defaults and optional overrides. + * + * @param {string} html + * @param {Record} [overrides] + */ +export const minifyHTML = async (html, overrides = {}) => { + const minified = minify(textEncoder.encode(html), { + ...DEFAULT_HTML_MINIFIER_OPTIONS, + ...overrides, + }); + + return textDecoder.decode(minified); +}; diff --git a/src/utils/queries/__tests__/index.test.mjs b/src/utils/queries/__tests__/index.test.mjs index 5a435487..a0e580a3 100644 --- a/src/utils/queries/__tests__/index.test.mjs +++ b/src/utils/queries/__tests__/index.test.mjs @@ -89,7 +89,6 @@ describe('createQueries', () => { }, ]; - console.log(node, definitions); queries.updateLinkReference(node, definitions); strictEqual(node.type, 'link');