diff --git a/.prettierignore b/.prettierignore index 492a816ce..b830eb822 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,6 +6,11 @@ !/scripts !/src !/tests +!/website +/website/.docusaurus +/website/docs +/website/readme-sources +/website/static tests/main-realpath/symlink/tsconfig.json tests/throw error.ts tests/throw error react tsx.tsx diff --git a/website/docs/module-type-overrides.md b/website/docs/module-type-overrides.md index 43ab19f55..05222ce2f 100644 --- a/website/docs/module-type-overrides.md +++ b/website/docs/module-type-overrides.md @@ -17,7 +17,7 @@ CommonJS or ESM. Node supports similar overriding via `.cjs` and `.mjs` file ex The following example tells ts-node to execute a webpack config as CommonJS: -```json title=tsconfig.json +```json title="tsconfig.json" { "ts-node": { "transpileOnly": true, diff --git a/website/docs/recipes/ava.md b/website/docs/recipes/ava.md index 945008ec0..83eda7446 100644 --- a/website/docs/recipes/ava.md +++ b/website/docs/recipes/ava.md @@ -8,7 +8,7 @@ Assuming you are configuring AVA via your `package.json`, add one of the followi Use this configuration if your `package.json` does not have `"type": "module"`. -```json title"package.json" +```json title="package.json" { "ava": { "extensions": [ @@ -25,7 +25,7 @@ Use this configuration if your `package.json` does not have `"type": "module"`. This configuration is necessary if your `package.json` has `"type": "module"`. -```json title"package.json" +```json title="package.json" { "ava": { "extensions": { diff --git a/website/docs/troubleshooting.md b/website/docs/troubleshooting.md index 2e2125c16..d007bcac4 100644 --- a/website/docs/troubleshooting.md +++ b/website/docs/troubleshooting.md @@ -71,7 +71,10 @@ the [tsconfig `"target"` option](https://www.typescriptlang.org/tsconfig#target) For example, `node` 12 does not understand the `?.` optional chaining operator. If you use `"target": "esnext"`, then the following TypeScript syntax: -```typescript +```typescript twoslash +export {}; +var foo: {bar: string} | undefined; +// ---cut--- const bar: string | undefined = foo?.bar; ``` diff --git a/website/docs/types.md b/website/docs/types.md index eaf425a93..cb1df0bea 100644 --- a/website/docs/types.md +++ b/website/docs/types.md @@ -28,7 +28,7 @@ Example project structure: Example module declaration file: -```typescript +```typescript twoslash declare module '' { // module definitions go here } @@ -36,7 +36,7 @@ declare module '' { For module definitions, you can use [`paths`](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping): -```json +```json title="tsconfig.json" { "compilerOptions": { "baseUrl": ".", @@ -49,9 +49,11 @@ For module definitions, you can use [`paths`](https://www.typescriptlang.org/doc An alternative approach for definitions of third-party libraries are [triple-slash directives](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html). This may be helpful if you prefer not to change your TypeScript `compilerOptions` or structure your custom type definitions when using `typeRoots`. Below is an example of the triple-slash directive as a relative path within your project: -```typescript -/// -import UntypedJsLib from "untyped_js_lib" +```typescript twoslash +/// +import {Greeter} from "untyped_js_lib" +const g = new Greeter(); +g.sayHello(); ``` **Tip:** If you _must_ use `files`, `include`, or `exclude`, enable `--files` flags or set `TS_NODE_FILES=true`. diff --git a/website/docs/usage.md b/website/docs/usage.md index 1e6e563e8..9b62841f8 100644 --- a/website/docs/usage.md +++ b/website/docs/usage.md @@ -32,7 +32,7 @@ ts-node-esm script.ts ## Shebang -```typescript +```typescript twoslash #!/usr/bin/env ts-node console.log("Hello, world!") @@ -40,7 +40,7 @@ console.log("Hello, world!") Passing options via shebang requires the [`env -S` flag](https://manpages.debian.org/bullseye/coreutils/env.1.en.html#S), which is available on recent versions of `env`. ([compatibility](https://github.com/TypeStrong/ts-node/pull/1448#issuecomment-913895766)) -```typescript +```typescript twoslash #!/usr/bin/env -S ts-node --files // This shebang works on Mac and Linux with newer versions of env // Technically, Mac allows omitting `-S`, but Linux requires it @@ -48,7 +48,7 @@ Passing options via shebang requires the [`env -S` flag](https://manpages.debian To write scripts with maximum portability, [specify all options in your `tsconfig.json`](./configuration#via-tsconfigjson-recommended) and omit them from the shebang. -```typescript +```typescript twoslash #!/usr/bin/env ts-node // This shebang works everywhere ``` diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 109116d2c..8c33ffcbc 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -19,7 +19,7 @@ module.exports = { // //isCloseable: false, // Defaults to `true`. // }, colorMode: { - respectPrefersColorScheme: true + respectPrefersColorScheme: true, }, navbar: { title: 'ts-node', @@ -61,6 +61,20 @@ module.exports = { }, ], }, + metadata: [ + { + name: 'msapplication-TileColor', + content: '#2b5797', + }, + { + name: 'msapplication-config', + content: '/ts-node/img/favicon/browserconfig.xml', + }, + { + name: 'theme-color', + content: '#ffffff', + }, + ], // footer: { // style: 'dark', // links: [ @@ -99,23 +113,14 @@ module.exports = { // // copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`, // }, prism: { - // for syntax highlighting - // additionalLanguages: ['powershell'], + // Note: these themes are ignored due to using shiki-twoslash + theme: require('prism-react-renderer/themes/vsLight'), + darkTheme: require('prism-react-renderer/themes/vsDark'), }, algolia: { - apiKey: 'c882a0a136ef4e15aa99db604280caa6', + appId: 'BYGNLKSCOV', + apiKey: '74ac2b781b0cf603c2f1b5e4f44e1c69', indexName: 'ts-node', - - // Optional: see doc section below - // contextualSearch: true, - - // Optional: see doc section below - // appId: 'YOUR_APP_ID', - - // Optional: Algolia search parameters - // searchParameters: {}, - - //... other Algolia params }, }, presets: [ @@ -124,19 +129,67 @@ module.exports = { { docs: { sidebarPath: require.resolve('./sidebars.js'), - editUrl: - 'https://github.com/TypeStrong/ts-node/edit/docs/website/', + editUrl: 'https://github.com/TypeStrong/ts-node/edit/docs/website/', }, - // blog: { - // showReadingTime: true, - // // Please change this to your repo. - // editUrl: - // 'https://github.com/facebook/docusaurus/edit/master/website/blog/', - // }, + blog: false, theme: { customCss: require.resolve('./src/css/custom.css'), }, }, ], + [ + 'docusaurus-preset-shiki-twoslash', + { + // https://github.com/shikijs/twoslash/blob/main/packages/shiki-twoslash/README.md#user-settings + + // langs: ["shell", "typescript", "javascript", "ts", "js", "tsx", "jsx", "json", "jsonc"], + includeJSDocInHover: true, + + themes: ['github-light', 'nord'], + + // VSCode default + // themes: ["light-plus", "dark-plus"], + + // Other options + // themes: ["min-light", "nord"], + // themes: ["min-light", "min-dark"], + // themes: ["github-light", "github-dark"], + // themes: ["solarized-light", "solarized-dark"], + }, + ], + ], + // Misleading API that probably will be refactored in Docusaurus, but this is + // simply a list of tags + stylesheets: [ + { + rel: 'apple-touch-icon', + sizes: '180x180', + href: '/ts-node/img/favicon/apple-touch-icon.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '32x32', + href: '/ts-node/img/favicon/favicon-32x32.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '16x16', + href: '/ts-node/img/favicon/favicon-16x16.png', + }, + { + rel: 'manifest', + href: '/ts-node/img/favicon/site.webmanifest', + }, + { + rel: 'mask-icon', + href: '/ts-node/img/favicon/safari-pinned-tab.svg', + color: '#5bbad5', + }, + { + rel: 'shortcut icon', + href: '/ts-node/img/favicon/favicon.ico', + }, ], }; diff --git a/website/package.json b/website/package.json index 6c3851cd3..c3545ea89 100644 --- a/website/package.json +++ b/website/package.json @@ -13,15 +13,15 @@ "build-readme": "./scripts/build-readme.mjs" }, "dependencies": { - "@docusaurus/core": "2.0.0-alpha.75", - "@docusaurus/preset-classic": "2.0.0-alpha.75", - "@docusaurus/theme-search-algolia": "^2.0.0-alpha.75", - "@mdx-js/react": "^1.6.21", + "@docusaurus/core": "2.0.0-beta.17", + "@docusaurus/preset-classic": "2.0.0-beta.17", + "@mdx-js/react": "^1.6.22", "@types/js-yaml": "^4.0.0", "clsx": "^1.1.1", + "docusaurus-preset-shiki-twoslash": "^1.1.36", "js-yaml": "^4.0.0", - "react": "^16.8.4", - "react-dom": "^16.8.4", + "react": "^17.0.2", + "react-dom": "^17.0.2", "remark": "^13.0.0", "remark-behead": "^2.3.3", "remark-frontmatter": "^3.0.0", @@ -42,5 +42,10 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "packageManager": "yarn@1.22.17", + "volta": { + "extends": "../package.json", + "yarn": "1.22.17" } } diff --git a/website/scripts/build-readme.mjs b/website/scripts/build-readme.mjs index b7cb22c48..50842776a 100755 --- a/website/scripts/build-readme.mjs +++ b/website/scripts/build-readme.mjs @@ -25,7 +25,7 @@ const readmePath = Path.resolve(__root, 'README.md'); const generateReadmeHeadersForCategories = { General: false, Advanced: true, - Recipes: true + Recipes: true, }; import sidebars from '../sidebars.js'; @@ -35,42 +35,50 @@ async function main() { await appendMarkdownFileToReadmeAst({ path: 'readme-sources/prefix.md', - headerLevel: 1 + headerLevel: 1, }); const sidebar = sidebars.primarySidebar; - for(const category of sidebar) { - const generateReadmeHeader = generateReadmeHeadersForCategories[category.label]; - if(generateReadmeHeader) { + for (const category of sidebar) { + const generateReadmeHeader = + generateReadmeHeadersForCategories[category.label]; + if (generateReadmeHeader) { readmeNodes.push(headerNode(1, category.label)); - } else if(generateReadmeHeader == null) { - throw new Error(`Update ${ import.meta.url } to include all sidebar categories`); + } else if (generateReadmeHeader == null) { + throw new Error( + `Update ${import.meta.url} to include all sidebar categories` + ); } - for(const page of category.items) { + for (const page of category.items) { await appendMarkdownFileToReadmeAst({ - path: `docs/${ page }.md`, - headerLevel: 1 + !!generateReadmeHeader + path: `docs/${page}.md`, + headerLevel: 1 + !!generateReadmeHeader, }); } } appendMarkdownFileToReadmeAst({ path: 'readme-sources/license.md', - headerLevel: 1 + headerLevel: 1, }); - async function appendMarkdownFileToReadmeAst({path, headerLevel}) { + async function appendMarkdownFileToReadmeAst({ path, headerLevel }) { const absPath = Path.resolve(__websiteRoot, path); - console.log(`Appending ${ path } at header level ${ headerLevel }`); + console.log(`Appending ${path} at header level ${headerLevel}`); const markdownSource = fs.readFileSync(absPath, 'utf8'); await remark() .use(remarkFrontmatter, ['yaml']) .use(parseFrontmatter) .use(remarkBehead, { after: '', depth: headerLevel - 1 }) .use(() => (ast) => { - const {frontmatter} = ast; - if(frontmatter && !frontmatter.omitHeaderOnMerge) { - readmeNodes.push(headerNode(headerLevel, frontmatter && frontmatter.title || Path.basename(absPath))); + const { frontmatter } = ast; + if (frontmatter && !frontmatter.omitHeaderOnMerge) { + readmeNodes.push( + headerNode( + headerLevel, + (frontmatter && frontmatter.title) || Path.basename(absPath) + ) + ); } readmeNodes.push(...ast.children); }) @@ -84,73 +92,93 @@ async function main() { .use(codeLanguageJsonToJsonc) .use(rewritePageLinksToAnchorLinks) .use(rewriteImgTargets) - .use(remarkToc, {tight: true}) - .process(vfile({ - path: readmePath, - contents: '' - })); + .use(trimCutFromTwoslashCode) + .use(remarkToc, { tight: true }) + .process( + vfile({ + path: readmePath, + contents: '', + }) + ); console.error(vfileReporter(renderedReadme)); - if(renderedReadme.messages.length) throw new Error('Aborting on diagnostics.'); + if (renderedReadme.messages.length) + throw new Error('Aborting on diagnostics.'); const lintResults = await remark() .use(remarkValidateLinks) .use(remarkRecommended) .process(renderedReadme); console.error(vfileReporter(lintResults)); - if(lintResults.messages.length) throw new Error('Aborting on diagnostics.'); + if (lintResults.messages.length) throw new Error('Aborting on diagnostics.'); fs.writeFileSync(readmePath, renderedReadme.contents); } function parseFrontmatter() { return (ast) => { - if(ast.children[0].type === 'yaml') { + if (ast.children[0].type === 'yaml') { ast.frontmatter = jsYaml.load(ast.children[0].value); ast.children.splice(0, 1); } - } + }; } function codeLanguageJsonToJsonc() { return (ast) => { - visit(ast, 'code', node => { - if(node.lang === 'json') node.lang = 'jsonc'; - }) - } + visit(ast, 'code', (node) => { + if (node.lang === 'json') node.lang = 'jsonc'; + }); + }; } function rewritePageLinksToAnchorLinks() { return (ast) => { - visit(ast, 'link', node => { - if(node.url?.match?.(/^https?\:\/\//)) return; + visit(ast, 'link', (node) => { + if (node.url?.match?.(/^https?\:\/\//)) return; // TODO take page title into account node.url = node.url.replace(/^[\.\/]*(?:([^#]+)|.*#(.*))$/, '#$1$2'); node.url = node.url.replace(/\.md$/, ''); }); - } + }; } function rewriteImgTargets() { return (ast) => { - visit(ast, 'image', node => { + visit(ast, 'image', (node) => { node.url = node.url.replace(/^\//, 'website/static/'); }); - } + }; +} + +function trimCutFromTwoslashCode() { + return (ast) => { + // Strip everything above // ---cut--- in twoslash code blocks + const lookingFor = '\n// ---cut---\n'; + visit(ast, 'code', (node) => { + if (node.meta?.includes('twoslash') && node.value.includes(lookingFor)) { + node.value = node.value.slice( + node.value.lastIndexOf(lookingFor) + lookingFor.length + ); + } + }); + }; } function headerNode(depth, value) { return { type: 'heading', depth, - children: [{ - type: 'text', - value, - children: [] - }] + children: [ + { + type: 'text', + value, + children: [], + }, + ], }; } try { await main(); -} catch(e) { +} catch (e) { console.error(e.message); process.exitCode = 1; } diff --git a/website/sidebars.js b/website/sidebars.js index 4f1d70e01..4e3dbd9f5 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -1,51 +1,55 @@ module.exports = { - primarySidebar: [{ - type: 'category', - label: 'General', - collapsed: false, - items: [ - 'overview', - 'installation', - 'usage', - 'configuration', - 'options', - 'imports', - 'troubleshooting', - 'performance', - ] - }, { - type: 'category', - label: 'Advanced', - collapsed: false, - items: [ - 'how-it-works', - 'paths', - 'types', - 'compilers', - 'transpilers', - 'module-type-overrides' - ], - }, { - type: 'category', - label: 'Recipes', - collapsed: false, - items: [ - 'recipes/watching-and-restarting', - 'recipes/ava', - 'recipes/gulp', - 'recipes/intellij', - 'recipes/mocha', - 'recipes/tape', - 'recipes/visual-studio-code', - 'recipes/other' - ] - }], - hiddenSidebar: [{ - type: 'category', - label: 'Hidden pages', - collapsed: false, - items: [ - 'options-table', - ] - }], + primarySidebar: [ + { + type: 'category', + label: 'General', + collapsed: false, + items: [ + 'overview', + 'installation', + 'usage', + 'configuration', + 'options', + 'imports', + 'troubleshooting', + 'performance', + ], + }, + { + type: 'category', + label: 'Advanced', + collapsed: false, + items: [ + 'how-it-works', + 'paths', + 'types', + 'compilers', + 'transpilers', + 'module-type-overrides', + ], + }, + { + type: 'category', + label: 'Recipes', + collapsed: false, + items: [ + 'recipes/watching-and-restarting', + 'recipes/ava', + 'recipes/gulp', + 'recipes/intellij', + 'recipes/mocha', + 'recipes/tape', + 'recipes/visual-studio-code', + 'recipes/other', + ], + }, + ], + hiddenSidebar: [ + { + type: 'category', + label: 'Hidden pages', + collapsed: false, + items: ['options-table'], + }, + ], }; diff --git a/website/src/css/custom.css b/website/src/css/custom.css index 9b7e4d80f..49c27214f 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -33,3 +33,24 @@ margin: 0 calc(-1 * var(--ifm-pre-padding)); padding: 0 var(--ifm-pre-padding); } + +/* Hide the external link icon */ +.navbar__item svg { + display: none; +} + +/* + * Shiki-twoslash + * See: https://github.com/shikijs/twoslash/tree/main/packages/docusaurus-preset-shiki-twoslash + */ +[data-theme='light'] .shiki.nord, +[data-theme='light'] .shiki.min-dark, +[data-theme='light'] .shiki.github-dark, +[data-theme='light'] .shiki.dark-plus, +[data-theme='light'] .shiki.solarized-dark, +[data-theme='dark'] .shiki.min-light, +[data-theme='dark'] .shiki.github-light, +[data-theme='dark'] .shiki.light-plus, +[data-theme='dark'] .shiki.solarized-light { + display: none; +} diff --git a/website/src/pages/index.js b/website/src/pages/index.js index 9a50c6e39..dcb410ad0 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -2,12 +2,11 @@ import React from 'react'; import clsx from 'clsx'; import Layout from '@theme/Layout'; import Link from '@docusaurus/Link'; -import Head from '@docusaurus/Head'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import useBaseUrl from '@docusaurus/useBaseUrl'; import styles from './styles.module.css'; -function Feature({imageUrl, title, description}) { +function Feature({ imageUrl, title, description }) { const imgUrl = useBaseUrl(imageUrl); return (
@@ -22,38 +21,28 @@ function Feature({imageUrl, title, description}) { function Home() { const context = useDocusaurusContext(); - const {siteConfig = {}} = context; + const { siteConfig = {} } = context; return ( - - - - - - - - - - - - +

{siteConfig.title}

{siteConfig.tagline}

+ to={useBaseUrl('docs/')} + > Get Started