diff --git a/package-lock.json b/package-lock.json index b814111f95b7..66e308f1b033 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,7 +45,7 @@ "rehype-slug": "~6.0.0", "semver": "~7.5.4", "sharp": "0.32.6", - "shiki": "^0.14.5", + "shiki": "^0.14.3", "tailwindcss": "^3.3.3", "turbo": "^1.10.14", "typescript": "~5.2.2", @@ -29305,9 +29305,9 @@ } }, "node_modules/shiki": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.5.tgz", - "integrity": "sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==", + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.3.tgz", + "integrity": "sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g==", "dependencies": { "ansi-sequence-parser": "^1.1.0", "jsonc-parser": "^3.2.0", diff --git a/package.json b/package.json index 52e29279a2b4..01c05168a52e 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "rehype-slug": "~6.0.0", "semver": "~7.5.4", "sharp": "0.32.6", - "shiki": "^0.14.5", + "shiki": "^0.14.3", "tailwindcss": "^3.3.3", "turbo": "^1.10.14", "typescript": "~5.2.2", diff --git a/pages/en/blog/release/v15.1.0.md b/pages/en/blog/release/v15.1.0.md index d96407a8daeb..92c60053022e 100644 --- a/pages/en/blog/release/v15.1.0.md +++ b/pages/en/blog/release/v15.1.0.md @@ -104,7 +104,7 @@ When generating snapshots, garbage collection may be triggered and bring the hea Generating V8 snapshots takes time and memory (both memory managed by the V8 heap and native memory outside the V8 heap). The bigger the heap is, the more resources it needs. Node.js will adjust the V8 heap to accommondate the additional V8 heap memory overhead, and try its best to avoid using up all the memory avialable to the process. -```console +```shell-session $ node --max-old-space-size=100 --heapsnapshot-near-heap-limit=3 index.js Wrote snapshot to Heap.20200430.100036.49580.0.001.heapsnapshot Wrote snapshot to Heap.20200430.100037.49580.0.002.heapsnapshot diff --git a/pages/en/blog/release/v18.16.0.md b/pages/en/blog/release/v18.16.0.md index 692bc615fc52..1fc74be3efc2 100644 --- a/pages/en/blog/release/v18.16.0.md +++ b/pages/en/blog/release/v18.16.0.md @@ -12,7 +12,7 @@ author: Danielle Adams Compile a JavaScript file into a single executable application: -```console +```shell-session $ echo 'console.log(`Hello, ${process.argv[2]}!`);' > hello.js $ cp $(command -v node) hello diff --git a/scripts/release-post/downloadsTable.mjs b/scripts/release-post/downloadsTable.mjs index 5b64290338e8..412ae8cfed97 100644 --- a/scripts/release-post/downloadsTable.mjs +++ b/scripts/release-post/downloadsTable.mjs @@ -2,7 +2,7 @@ import semVer from 'semver'; -const downloadOptions = [ +const allDownloads = [ { title: 'Windows 32-bit Installer', templateUrl: 'https://nodejs.org/dist/v%version%/node-v%version%-x86.msi', @@ -41,6 +41,11 @@ const downloadOptions = [ templateUrl: 'https://nodejs.org/dist/v%version%/node-v%version%-darwin-x64.tar.gz', }, + { + title: 'Linux 32-bit Binary', + templateUrl: + 'https://nodejs.org/dist/v%version%/node-v%version%-linux-x86.tar.xz', + }, { title: 'Linux 64-bit Binary', templateUrl: @@ -51,6 +56,11 @@ const downloadOptions = [ templateUrl: 'https://nodejs.org/dist/v%version%/node-v%version%-linux-ppc64le.tar.xz', }, + { + title: 'Linux PPC BE 64-bit Binary', + templateUrl: + 'https://nodejs.org/dist/v%version%/node-v%version%-linux-ppc64.tar.xz', + }, { title: 'Linux s390x 64-bit Binary', templateUrl: @@ -61,6 +71,21 @@ const downloadOptions = [ templateUrl: 'https://nodejs.org/dist/v%version%/node-v%version%-aix-ppc64.tar.gz', }, + { + title: 'SmartOS 32-bit Binary', + templateUrl: + 'https://nodejs.org/dist/v%version%/node-v%version%-sunos-x86.tar.xz', + }, + { + title: 'SmartOS 64-bit Binary', + templateUrl: + 'https://nodejs.org/dist/v%version%/node-v%version%-sunos-x64.tar.xz', + }, + { + title: 'ARMv6 32-bit Binary', + templateUrl: + 'https://nodejs.org/dist/v%version%/node-v%version%-linux-armv6l.tar.xz', + }, { title: 'ARMv7 32-bit Binary', templateUrl: @@ -77,13 +102,98 @@ const downloadOptions = [ }, ]; +// v0.x of Node.js +const legacyDownloads = [ + { + title: 'Windows 32-bit Installer', + templateUrl: 'https://nodejs.org/dist/v%version%/node-v%version%-x86.msi', + }, + { + title: 'Windows 64-bit Installer', + templateUrl: + 'https://nodejs.org/dist/v%version%/x64/node-v%version%-x64.msi', + }, + { + title: 'Windows 32-bit Binary', + templateUrl: 'https://nodejs.org/dist/v%version%/node.exe', + }, + { + title: 'Windows 64-bit Binary', + templateUrl: 'https://nodejs.org/dist/v%version%/x64/node.exe', + }, + { + title: 'macOS Universal Installer', + templateUrl: 'https://nodejs.org/dist/v%version%/node-v%version%.pkg', + }, + { + title: 'macOS 64-bit Binary', + templateUrl: + 'https://nodejs.org/dist/v%version%/node-v%version%-darwin-x64.tar.gz', + }, + { + title: 'macOS 32-bit Binary', + templateUrl: + 'https://nodejs.org/dist/v%version%/node-v%version%-darwin-x86.tar.gz', + }, + { + title: 'Linux 32-bit Binary', + templateUrl: + 'https://nodejs.org/dist/v%version%/node-v%version%-linux-x86.tar.gz', + }, + { + title: 'Linux 64-bit Binary', + templateUrl: + 'https://nodejs.org/dist/v%version%/node-v%version%-linux-x64.tar.gz', + }, + { + title: 'SmartOS 32-bit Binary', + templateUrl: + 'https://nodejs.org/dist/v%version%/node-v%version%-sunos-x86.tar.gz', + }, + { + title: 'SmartOS 64-bit Binary', + templateUrl: + 'https://nodejs.org/dist/v%version%/node-v%version%-sunos-x64.tar.gz', + }, + { + title: 'Source Code', + templateUrl: 'https://nodejs.org/dist/v%version%/node-v%version%.tar.gz', + }, +]; + const resolveUrl = (item, version) => { const url = item.templateUrl.replace(/%version%/g, version); return Object.assign({ url }, item); }; const resolveDownloads = version => { - let downloads = downloadOptions; + let downloads = allDownloads; + + if (semVer.satisfies(version, '< 1.0.0')) { + return legacyDownloads; + } + + if (semVer.satisfies(version, '>= 8.0.0')) { + downloads = downloads.filter( + ver => ver.title !== 'Linux PPC BE 64-bit Binary' + ); + } + + if (semVer.satisfies(version, '>= 10.0.0')) { + downloads = downloads.filter( + ver => + ver.title !== 'Linux 32-bit Binary' && + ver.title !== 'SmartOS 32-bit Binary' + ); + } + + if (semVer.satisfies(version, '>= 12.0.0')) { + downloads = downloads.filter(ver => ver.title !== 'ARMv6 32-bit Binary'); + } + + if (semVer.satisfies(version, '>= 14.0.0')) { + downloads = downloads.filter(ver => ver.title !== 'SmartOS 64-bit Binary'); + } if (semVer.satisfies(version, '< 16.0.0')) { downloads = downloads.filter( @@ -102,5 +212,7 @@ const resolveDownloads = version => { return downloads; }; -export const downloadsTable = version => +const downloadsTable = version => resolveDownloads(version).map(item => resolveUrl(item, version)); + +export default downloadsTable; diff --git a/scripts/release-post/index.mjs b/scripts/release-post/index.mjs index 2b6fb831e8ed..ea5b717975af 100644 --- a/scripts/release-post/index.mjs +++ b/scripts/release-post/index.mjs @@ -20,98 +20,75 @@ 'use strict'; -import { existsSync, readFileSync } from 'node:fs'; -import { writeFile } from 'node:fs/promises'; -import { resolve } from 'node:path'; +import fs from 'node:fs'; +import path from 'node:path'; +import url from 'node:url'; import handlebars from 'handlebars'; -import { downloadsTable } from './downloadsTable.mjs'; +import downloadsTable from './downloadsTable.mjs'; import { getRelativePath } from '../../next.helpers.mjs'; -const URLS = { - NODE_DIST_JSON: 'https://nodejs.org/dist/index.json', - GITHUB_PROFILE: author => `https://api.github.com/users/${author}`, - NODE_CHANGELOG_MD: releaseLine => - `https://raw.githubusercontent.com/nodejs/node/main/doc/changelogs/CHANGELOG_V${releaseLine}.md`, - NODE_SHASUM: version => - `https://nodejs.org/dist/v${version}/SHASUMS256.txt.asc`, -}; - -const ERRORS = { - NO_VERSION_PROVIDED: new Error('No version provided'), - RELEASE_EXISTS: version => - new Error(`Release post for ${version} already exists!`), - NO_AUTHOR_FOUND: version => - new Error(`Couldn't find @author of ${version} release :(`), - NO_VERSION_POLICY: version => - new Error(`Could not find version policy of ${version} in its changelog`), - NO_CHANGELOG_BODY: version => - new Error(`Could not find changelog body of ${version} release`), - NO_CHANGELOG_FOUND: version => - new Error(`Couldn't find matching changelog for ${version}`), - INVALID_STATUS_CODE: (url, status) => - new Error(`Invalid status (!= 200) while retrieving ${url}: ${status}`), - FAILED_FILE_CREATION: reason => - new Error(`Failed to write Release post: Reason: ${reason}`), -}; - -const ARGS = { - CURRENT_PATH: process.argv[1], - SPECIFIC_VERSION: process.argv[2] && process.argv[2].replace('--force', ''), - SHOULD_FORCE: (process.argv[3] || process.argv[2]) === '--force', -}; - // this allows us to get the current module working directory const __dirname = getRelativePath(import.meta.url); -const request = options => { +const sendRequest = opts => { + const options = { + headers: { 'User-Agent': 'nodejs.org release blog post script' }, + ...opts, + }; + return fetch(options.url, options).then(resp => { if (resp.status !== 200) { - throw ERRORS.INVALID_STATUS_CODE(options.url, resp.status); + throw new Error( + `Invalid status code (!= 200) while retrieving ${options.url}: ${resp.status}` + ); } return options.json ? resp.json() : resp.text(); }); }; -const explicitVersion = version => - new Promise((resolve, reject) => - version && version.length > 0 - ? resolve(version) - : reject(ERRORS.NO_VERSION_PROVIDED) - ); +const explicitVersion = version => { + return version + ? Promise.resolve(version) + : Promise.reject(new Error('Invalid "version" argument')); +}; -const findLatestVersion = () => - request({ url: URLS.NODE_DIST_JSON, json: true }) - .then(versions => versions.length && versions[0]) - .then(({ version }) => version.substr(1)); +const findLatestVersion = () => { + return sendRequest({ + url: 'https://nodejs.org/dist/index.json', + json: true, + }).then(versions => versions[0].version.substr(1)); +}; const fetchDocs = version => { - const blogPostPieces = [ + return Promise.all([ fetchChangelogBody(version), fetchAuthor(version), fetchVersionPolicy(version), fetchShasums(version), verifyDownloads(version), - ]; + ]).then(results => { + const [changelog, author, versionPolicy, shasums, files] = results; - return Promise.all(blogPostPieces).then( - ([changelog, author, versionPolicy, shasums, files]) => ({ + return { version, changelog, author, versionPolicy, shasums, files, - }) - ); + }; + }); }; const fetchAuthor = version => { return fetchChangelog(version) .then(section => findAuthorLogin(version, section)) - .then(author => request({ url: URLS.GITHUB_PROFILE(author), json: true })) + .then(author => + sendRequest({ url: `https://api.github.com/users/${author}`, json: true }) + ) .then(githubRes => githubRes.name); }; @@ -119,7 +96,9 @@ const fetchChangelog = version => { const parts = version.split('.'); const releaseLine = parts[0] === '0' ? parts.slice(0, 2).join('') : parts[0]; - return request({ url: URLS.NODE_CHANGELOG_MD(releaseLine) }).then(data => { + return sendRequest({ + url: `https://raw.githubusercontent.com/nodejs/node/main/doc/changelogs/CHANGELOG_V${releaseLine}.md`, + }).then(data => { // matches a complete release section const rxSection = new RegExp( `\\n([\\s\\S]+?)(?:\\n - str.replace(/^([ ]{0,4})(\* )/gm, '$1- '); + if (!matches) { + matches = rxFallbackSectionBody.exec(section); + } - return new Promise((resolve, reject) => - matches.length && matches[1] - ? resolve(replaceAsteriskLists(matches[1].trim())) - : reject(ERRORS.NO_CHANGELOG_BODY(version)) - ); + return matches + ? matches[1].trim().replace(rxSectionConsole, '```shell-session') + : Promise.reject( + new Error(`Could not find changelog body of ${version} release`) + ); }); }; @@ -164,23 +145,28 @@ const fetchVersionPolicy = version => { // ## 2015-12-04, Version 0.12.9 (LTS), @rvagg const rxPolicy = /^## ?\d{4}-\d{2}-\d{2}, Version [^(].*\(([^)]+)\)/; const matches = rxPolicy.exec(section); - - return new Promise((resolve, reject) => - matches.length && matches[1] - ? resolve(matches[1]) - : reject(ERRORS.NO_VERSION_POLICY(version)) - ); + return matches + ? matches[1] + : Promise.reject( + new Error( + `Could not find version policy of ${version} in its changelog` + ) + ); }); }; -const fetchShasums = version => - request({ url: URLS.NODE_SHASUM(version) }).then( - result => result.trim(), - () => '[INSERT SHASUMS HERE]' - ); +const fetchShasums = version => { + return sendRequest({ + url: `https://nodejs.org/dist/v${version}/SHASUMS256.txt.asc`, + }).then(null, () => '[INSERT SHASUMS HERE]'); +}; -const verifyDownloads = version => - Promise.all(downloadsTable(version).map(urlOrComingSoon)); +const verifyDownloads = version => { + const allDownloads = downloadsTable(version); + const reqs = allDownloads.map(urlOrComingSoon); + + return Promise.all(reqs); +}; const findAuthorLogin = (version, section) => { // looking for the @author part of the release header, eg: @@ -190,57 +176,82 @@ const findAuthorLogin = (version, section) => { const rxReleaseAuthor = /^## .*? \([^)]+\)[,.] @(\S+)/; const matches = rxReleaseAuthor.exec(section); - return new Promise((resolve, reject) => - matches.length && matches[1] - ? resolve(matches[1]) - : reject(ERRORS.RELEASE_EXISTS(version)) - ); + return matches + ? matches[1] + : Promise.reject( + new Error(`Couldn't find @author of ${version} release :(`) + ); }; const urlOrComingSoon = binary => { - return request({ url: binary.url, method: 'HEAD' }).then( + const url = binary.url.replace('nodejs.org', 'direct.nodejs.org'); + + return sendRequest({ url, method: 'HEAD' }).then( () => `${binary.title}: ${binary.url}`, - () => `${binary.title}: *Coming soon*` + () => { + console.log(`\x1B[32m "${binary.title}" is Coming soon...\x1B[39m`); + return `${binary.title}: *Coming soon*`; + } ); }; const renderPost = results => { - const blogTemplateSource = readFileSync( - resolve(__dirname, 'template.hbs'), - 'utf8' + const templateStr = fs + .readFileSync(path.resolve(__dirname, 'template.hbs')) + .toString('utf8'); + + const template = handlebars.compile(templateStr, { noEscape: true }); + + const view = Object.assign( + { + date: new Date().toISOString(), + versionSlug: slugify(results.version), + }, + results ); - const template = handlebars.compile(blogTemplateSource, { noEscape: true }); - - const templateParameters = { - date: new Date().toISOString(), - versionSlug: slugify(results.version), - ...results, - }; - - return { content: template(templateParameters), ...results }; + return Object.assign( + { + content: template(view), + }, + results + ); }; const writeToFile = results => { - const blogPostPath = resolve( + const filepath = path.resolve( __dirname, - '../../pages/en/blog/release', + '..', + '..', + 'pages', + 'en', + 'blog', + 'release', `v${results.version}.md` ); return new Promise((resolve, reject) => { - if (existsSync(blogPostPath) && !ARGS.SHOULD_FORCE) { - reject(ERRORS.RELEASE_EXISTS(results.version)); - return; + if (fs.existsSync(filepath) && process.argv[3] !== '--force') { + return reject( + new Error(`Release post for ${results.version} already exists!`) + ); } - writeFile(blogPostPath, results.content) - .then(() => resolve(blogPostPath)) - .catch(error => reject(ERRORS.FAILED_FILE_CREATION(error.message))); + try { + fs.writeFileSync(filepath, results.content); + } catch (error) { + return reject( + new Error(`Failed to write Release post: Reason: ${error.message}`) + ); + } + + resolve(filepath); }); }; -const slugify = str => str.replace(/\./g, '-'); +const slugify = str => { + return str.replace(/\./g, '-'); +}; export { explicitVersion, @@ -256,15 +267,22 @@ export { // This allows us to verify that the script is being run directly from node.js/cli if (import.meta.url.startsWith('file:')) { - if (ARGS.CURRENT_PATH === `${__dirname}index.mjs`) { - explicitVersion(ARGS.SPECIFIC_VERSION) + const modulePath = url.fileURLToPath(import.meta.url); + + if (process.argv[1] === modulePath) { + explicitVersion(process.argv[2]) .then(null, findLatestVersion) .then(fetchDocs) .then(renderPost) .then(writeToFile) .then( - filepath => console.log('Release post created:', filepath), - error => console.error('Some error occurred here!', error.stack) + filepath => { + console.log('Release post created:', filepath); + }, + err => { + console.error('Some error occurred here!', err.stack); + process.exit(1); + } ); } } diff --git a/scripts/release-post/template.hbs b/scripts/release-post/template.hbs index 29d075e8b9d9..39b38db442fd 100644 --- a/scripts/release-post/template.hbs +++ b/scripts/release-post/template.hbs @@ -16,6 +16,6 @@ Documentation: https://nodejs.org/docs/v{{version}}/api/ ### SHASUMS -```text +``` {{shasums}} ``` diff --git a/shiki.config.mjs b/shiki.config.mjs index 021ea96840d8..dfc8b6d87143 100644 --- a/shiki.config.mjs +++ b/shiki.config.mjs @@ -15,8 +15,6 @@ import javaScriptLanguage from 'shiki/languages/javascript.tmLanguage.json' assert { type: 'json' }; import jsonLanguage from 'shiki/languages/json.tmLanguage.json' assert { type: 'json' }; import jsxLanguage from 'shiki/languages/jsx.tmLanguage.json' assert { type: 'json' }; -import shellScriptLanguage from 'shiki/languages/shellscript.tmLanguage.json' assert { type: 'json' }; -import shellSessionLanguage from 'shiki/languages/shellsession.tmLanguage.json' assert { type: 'json' }; import typeScriptLanguage from 'shiki/languages/typescript.tmLanguage.json' assert { type: 'json' }; import xmlLanguage from 'shiki/languages/xml.tmLanguage.json' assert { type: 'json' }; import yamlLanguage from 'shiki/languages/yaml.tmLanguage.json' assert { type: 'json' }; @@ -26,8 +24,8 @@ export const SUPPORTED_LANGUAGES = [ { id: 'javascript', scopeName: 'source.js', - grammar: javaScriptLanguage, aliases: ['js'], + grammar: javaScriptLanguage, }, { id: 'json', @@ -42,8 +40,8 @@ export const SUPPORTED_LANGUAGES = [ { id: 'typescript', scopeName: 'source.ts', - grammar: typeScriptLanguage, aliases: ['ts'], + grammar: typeScriptLanguage, }, { id: 'xml', @@ -54,18 +52,5 @@ export const SUPPORTED_LANGUAGES = [ id: 'yaml', scopeName: 'source.yaml', grammar: yamlLanguage, - aliases: ['yml'], - }, - { - id: 'shellscript', - scopeName: 'source.shell', - grammar: shellScriptLanguage, - aliases: ['bash', 'sh', 'shell', 'zsh'], - }, - { - id: 'shellsession', - scopeName: 'text.shell-session', - grammar: shellSessionLanguage, - aliases: ['console'], }, ];