diff --git a/package-lock.json b/package-lock.json index 3e0e2250ef..6372661a85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -240,7 +240,6 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz", "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==", "dev": true, - "peer": true, "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -401,6 +400,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -586,6 +586,7 @@ "version": "7.23.3", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz", "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -623,12 +624,14 @@ "node_modules/@babel/core/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "peer": true }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -662,6 +665,7 @@ "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "peer": true, "dependencies": { "@babel/compat-data": "^7.22.9", "@babel/helper-validator-option": "^7.22.15", @@ -677,6 +681,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "peer": true, "dependencies": { "yallist": "^3.0.2" } @@ -685,6 +690,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -692,7 +698,8 @@ "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "peer": true }, "node_modules/@babel/helper-environment-visitor": { "version": "7.22.20", @@ -740,6 +747,7 @@ "version": "7.23.3", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "peer": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-module-imports": "^7.22.15", @@ -766,6 +774,7 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "peer": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -804,6 +813,7 @@ "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -812,6 +822,7 @@ "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", + "peer": true, "dependencies": { "@babel/template": "^7.26.9", "@babel/types": "^7.26.10" @@ -2695,7 +2706,6 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.2.tgz", "integrity": "sha512-ODsoD39Lq6vR6aBgvjTnA3nZGliknKboc9Gtxr7E4WDNqY24MxANKcuDQSF0jzapvGb3KWOEDrKfve4HoWGK+g==", - "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.1", @@ -3343,7 +3353,6 @@ "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "playwright": "1.56.1" }, @@ -4155,7 +4164,6 @@ "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", @@ -4317,7 +4325,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.20.tgz", "integrity": "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4329,7 +4336,6 @@ "integrity": "sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -4500,7 +4506,6 @@ "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.39.1", "@typescript-eslint/types": "8.39.1", @@ -5139,7 +5144,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5169,7 +5173,6 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -5669,7 +5672,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001733", "electron-to-chromium": "^1.5.199", @@ -5920,7 +5922,6 @@ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", "license": "MIT", - "peer": true, "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", @@ -7142,7 +7143,6 @@ "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -7204,7 +7204,6 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -7463,7 +7462,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -8623,6 +8621,7 @@ "node_modules/gensync": { "version": "1.0.0-beta.2", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -8828,7 +8827,6 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", "dev": true, - "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -10203,7 +10201,6 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, - "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -10300,6 +10297,7 @@ "node_modules/json5": { "version": "2.2.3", "license": "MIT", + "peer": true, "bin": { "json5": "lib/cli.js" }, @@ -13129,7 +13127,6 @@ "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "playwright-core": "cli.js" }, @@ -13193,7 +13190,6 @@ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -13363,7 +13359,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -13384,7 +13379,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -14052,7 +14046,6 @@ "integrity": "sha512-d0NoFH4v6SjEK7BoX810Jsrhj7IQSYHAHLi/iSpgqKc7LaIDshFRlSg5LOymf9FqQhxEHs2W5ZQXlvy0KD45Uw==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -14975,7 +14968,6 @@ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.0.0", "@babel/traverse": "^7.4.5", @@ -15222,7 +15214,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -15538,7 +15529,6 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15886,7 +15876,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "napi-postinstall": "^0.2.2" }, @@ -16089,7 +16078,6 @@ "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -16198,7 +16186,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, diff --git a/package.json b/package.json index c4ced9d677..6371b1144e 100644 --- a/package.json +++ b/package.json @@ -19,11 +19,13 @@ "build": "next build --webpack", "check-content-type": "tsx src/workflows/check-content-type.ts", "check-github-github-links": "tsx src/links/scripts/check-github-github-links.ts", + "clone-early-access": "./src/early-access/scripts/clone-locally", "clone-translations": "./src/languages/scripts/clone-translations.sh", "cmp-files": "tsx src/workflows/cmp-files.ts", "content-changes-table-comment": "tsx src/workflows/content-changes-table-comment.ts", "copy-fixture-data": "tsx src/tests/scripts/copy-fixture-data.ts", "count-translation-corruptions": "cross-env NODE_OPTIONS=--max-old-space-size=8192 tsx src/languages/scripts/count-translation-corruptions.ts", + "create-early-access-branch": "./src/early-access/scripts/create-branch", "create-enterprise-issue": "tsx src/ghes-releases/scripts/create-enterprise-issue.ts", "cta-builder": "tsx src/content-render/scripts/cta-builder.ts", "debug": "cross-env NODE_ENV=development ENABLED_LANGUAGES=en nodemon --inspect src/frame/server.ts", @@ -102,7 +104,8 @@ "validate-asset-images": "tsx src/assets/scripts/validate-asset-images.ts", "validate-github-github-docs-urls": "tsx src/links/scripts/validate-github-github-docs-urls/index.ts", "warmup-remotejson": "tsx src/archives/scripts/warmup-remotejson.ts", - "what-docs-early-access-branch": "tsx src/early-access/scripts/what-docs-early-access-branch.ts" + "what-docs-early-access-branch": "tsx src/early-access/scripts/what-docs-early-access-branch.ts", + "writers": "tsx src/workflows/writers-help-metadata.ts" }, "lint-staged": { "*.{ts,tsx}": "eslint --cache --fix", diff --git a/src/content-linter/scripts/find-unsed-variables.ts b/src/content-linter/scripts/find-unsed-variables.ts index 4fbd0ff085..29a409c6bb 100644 --- a/src/content-linter/scripts/find-unsed-variables.ts +++ b/src/content-linter/scripts/find-unsed-variables.ts @@ -1,6 +1,8 @@ /** - * This script iterates over all pages and all reusables and looks for - * mentions of variables in Liquid syntax. For example, + * @purpose Writer tool + * @description Look for mentions of variables in Liquid syntax across all pages + * + * For example, * * --- * title: '{% data variables.product.prodname_mobile %} is cool' diff --git a/src/content-linter/scripts/lint-content.ts b/src/content-linter/scripts/lint-content.ts index 60b2264e0b..df1544fa53 100755 --- a/src/content-linter/scripts/lint-content.ts +++ b/src/content-linter/scripts/lint-content.ts @@ -1,3 +1,7 @@ +/** + * @purpose Writer tool + * @description Run the Docs content linter, specifying paths and optional rules + */ // @ts-nocheck import fs from 'fs' import path from 'path' diff --git a/src/content-render/scripts/add-content-type.ts b/src/content-render/scripts/add-content-type.ts index 15c642f697..ead62e3f0c 100644 --- a/src/content-render/scripts/add-content-type.ts +++ b/src/content-render/scripts/add-content-type.ts @@ -1,7 +1,7 @@ -// This script auto-populates the `contentType` frontmatter property based on -// the directory location of the content file. -// Run with: -// npm run-script -- add-content-type --help +/** + * @purpose Writer tool + * @description Auto-populate the `contentType` frontmatter property based on the directory location of the content file + */ import fs from 'fs' import path from 'path' diff --git a/src/content-render/scripts/cta-builder.ts b/src/content-render/scripts/cta-builder.ts index f5aae60970..9a03ce8e54 100644 --- a/src/content-render/scripts/cta-builder.ts +++ b/src/content-render/scripts/cta-builder.ts @@ -1,3 +1,7 @@ +/** + * @purpose Writer tool + * @description Create a properly formatted Call-to-Action URL with tracking parameters + */ import { Command } from 'commander' import readline from 'readline' import chalk from 'chalk' diff --git a/src/content-render/scripts/move-content.ts b/src/content-render/scripts/move-content.ts index f4b7c86eb6..5454ace063 100755 --- a/src/content-render/scripts/move-content.ts +++ b/src/content-render/scripts/move-content.ts @@ -1,3 +1,7 @@ +/** + * @purpose Writer tool + * @description Move or rename a file or a folder and automatically add redirects + */ // @ts-nocheck // [start-readme] // diff --git a/src/content-render/scripts/reusables-cli.ts b/src/content-render/scripts/reusables-cli.ts index 0d5990b953..d253cd6d2a 100644 --- a/src/content-render/scripts/reusables-cli.ts +++ b/src/content-render/scripts/reusables-cli.ts @@ -1,3 +1,7 @@ +/** + * @purpose Writer tool + * @description Find all content files that use a specific reusable + */ // Usage: npm run reusables -- --help // Usage: npm run reusables -- find used accounts/create-account.md // Usage: npm run reusables -- find unused accounts/create-account.md diff --git a/src/content-render/scripts/update-filepaths.ts b/src/content-render/scripts/update-filepaths.ts index 221a1522dc..f866930106 100755 --- a/src/content-render/scripts/update-filepaths.ts +++ b/src/content-render/scripts/update-filepaths.ts @@ -1,10 +1,7 @@ -// [start-readme] -// -// Run this script to update filepaths to match short titles (or titles as a fallback). -// Use -// npm run-script -- update-filepaths --help -// -// [end-readme] +/** + * @purpose Writer tool + * @description Update content filenames to match short titles + */ import fs from 'fs' import path from 'path' diff --git a/src/dev-toc/generate.ts b/src/dev-toc/generate.ts index 5e6afa955e..f836d8afa2 100644 --- a/src/dev-toc/generate.ts +++ b/src/dev-toc/generate.ts @@ -1,5 +1,6 @@ /** - * Development tool that generates a local Table of Contents (TOC) for the GitHub Docs website. + * @purpose Writer tool + * @description Generate a local table of contents for the GitHub Docs website * * This script creates static HTML files for each documentation version, renders page titles * using Liquid templating, and opens the generated TOC in your browser for easy navigation diff --git a/src/early-access/scripts/clone-locally b/src/early-access/scripts/clone-locally index ac6d135c15..ab816c586d 100755 --- a/src/early-access/scripts/clone-locally +++ b/src/early-access/scripts/clone-locally @@ -1,10 +1,7 @@ #!/usr/bin/env bash -# [start-readme] -# -# This script is run on a writer's machine to begin developing Early Access content locally. -# -# [end-readme] +# @purpose Writer tool +# @description Clone the docs-early-access repo set -e diff --git a/src/early-access/scripts/create-branch b/src/early-access/scripts/create-branch index c984267aff..c5f6fb6fad 100755 --- a/src/early-access/scripts/create-branch +++ b/src/early-access/scripts/create-branch @@ -1,10 +1,7 @@ #!/usr/bin/env bash -# [start-readme] -# -# This script is run on a writer's machine to create an Early Access branch that matches the current docs-internal branch. -# -# [end-readme] +# @purpose Writer tool +# @description Create matching branches in docs-early-access and docs-internal set -e diff --git a/src/early-access/scripts/symlink-from-local-repo.ts b/src/early-access/scripts/symlink-from-local-repo.ts index 7cb192c6f2..377421d693 100644 --- a/src/early-access/scripts/symlink-from-local-repo.ts +++ b/src/early-access/scripts/symlink-from-local-repo.ts @@ -1,10 +1,7 @@ -// [start-readme] -// -// This script is run on a writer's machine while developing Early Access content locally. -// You must pass the script the location of your local copy of -// the `github/docs-early-access` git repo as the first argument. -// -// [end-readme] +/** + * @purpose Writer tool + * @description Create or destroy symlinks to your local docs-early-access checkout + */ import { rimraf } from 'rimraf' import fs from 'fs' diff --git a/src/early-access/scripts/update-data-and-image-paths.ts b/src/early-access/scripts/update-data-and-image-paths.ts index 18674ae209..254533c3c6 100644 --- a/src/early-access/scripts/update-data-and-image-paths.ts +++ b/src/early-access/scripts/update-data-and-image-paths.ts @@ -1,9 +1,7 @@ -// [start-readme] -// -// This script is run on a writer's machine while developing Early Access content locally. It -// updates the data and image paths to either include `early-access` or remove it. -// -// [end-readme] +/** + * @purpose Writer tool + * @description Add or remove "early-access" from data and image paths + */ import fs from 'fs' import path from 'path' diff --git a/src/metrics/scripts/docsaudit.ts b/src/metrics/scripts/docsaudit.ts index 75ef1d552f..4b90e304c6 100644 --- a/src/metrics/scripts/docsaudit.ts +++ b/src/metrics/scripts/docsaudit.ts @@ -1,3 +1,8 @@ +/** + * @purpose Writer tool + * @description Get data about a top-level docs product and output a CSV + */ + import fs from 'fs' import path from 'path' import { fileURLToPath } from 'url' diff --git a/src/metrics/scripts/docstat.ts b/src/metrics/scripts/docstat.ts index 2e291ccc4a..f21f67a68d 100644 --- a/src/metrics/scripts/docstat.ts +++ b/src/metrics/scripts/docstat.ts @@ -1,3 +1,8 @@ +/** + * @purpose Writer tool + * @description Get a data snapshot of a given Docs URL for the last 30 days or specified period + */ + import fs from 'fs' import path from 'path' import { Command } from 'commander' diff --git a/src/workflows/writers-help-metadata.ts b/src/workflows/writers-help-metadata.ts new file mode 100644 index 0000000000..ae535f216c --- /dev/null +++ b/src/workflows/writers-help-metadata.ts @@ -0,0 +1,215 @@ +#!/usr/bin/env tsx + +import { readFileSync } from 'fs' +import { glob } from 'glob' +import path from 'path' +import { fileURLToPath } from 'url' + +const __scriptname = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__scriptname) + +const PURPOSE_STRING = '@purpose Writer tool' +const DESCRIPTION_STRING = '@description' +const DESCRIPTION_REGEX = new RegExp(`${DESCRIPTION_STRING}\\s+(.+)`) + +interface WriterTool { + name: string + description: string + priority?: number // Lower numbers = higher priority +} + +interface WriterToolsCollection { + [category: string]: WriterTool[] +} + +interface ScriptMetadata { + isWriterTool?: boolean + category?: string + description?: string +} + +// Manual entries for scripts that aren't TypeScript files with metadata +const MANUAL_ENTRIES: WriterToolsCollection = { + 'Validation and formatting': [ + { name: 'prettier', description: 'Format markdown, YAML, and other files' }, + ], + Development: [ + { name: 'dev', description: 'Start local development server' }, + { name: 'build', description: 'Build the application' }, + ], +} + +async function discoverWriterTools(): Promise { + const packageJsonPath = path.join(__dirname, '..', '..', 'package.json') + const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) + const tools: WriterToolsCollection = { ...MANUAL_ENTRIES } // Start with manual entries + + // First get all files + const allFiles = await glob('src/**/*', { + cwd: path.join(__dirname, '..', '..'), + absolute: true, + ignore: ['**/node_modules/**', '**/tests/**', '**/test/**', '**/.*'], + }) + + // Then filter for .ts, .js, .sh scripts + const scriptFiles = allFiles.filter((file) => { + if (file === __scriptname) return false // skip the current file + + const ext = path.extname(file) + if (['.ts', '.js', '.sh'].includes(ext)) return true + + // For extensionless files, check if they're executable or have shebang + if (ext === '') { + try { + const content = readFileSync(file, 'utf8') + return content.startsWith('#!/bin/bash') || content.startsWith('#!/usr/bin/env bash') + } catch { + return false + } + } + return false + }) + + for (const filePath of scriptFiles) { + try { + const relativePath = path.relative(process.cwd(), filePath) + const content = readFileSync(filePath, 'utf8') + const metadata = extractMetadata(content) + + if (metadata.isWriterTool) { + metadata.category = getCategory(relativePath) + // Find corresponding npm script + const scriptName = findScriptName(packageJson.scripts, relativePath) + if (scriptName) { + if (!tools[metadata.category]) tools[metadata.category] = [] + + // Check if not already added manually + const exists = tools[metadata.category].some((tool) => tool.name === scriptName) + if (!exists) { + tools[metadata.category].push({ + name: scriptName, + description: metadata.description || `${scriptName} tool`, + }) + } + } + } + } catch { + // Skip files that can't be read + continue + } + } + + return tools +} + +function extractMetadata(content: string): ScriptMetadata { + const metadata: ScriptMetadata = {} + const lines = content.split('\n').slice(0, 20) // Only check first 20 lines + + for (const line of lines) { + if (line.includes(PURPOSE_STRING)) { + metadata.isWriterTool = true + } + + if (line.includes(DESCRIPTION_STRING)) { + // Extract description from line like "@description Add content type frontmatter to articles" + const match = line.match(DESCRIPTION_REGEX) + if (match) { + metadata.description = match[1].trim() + } + } + } + + return metadata +} + +// Convert the DIR in src/DIR/ to a title-cased category name +// E.g. src/secret-scanning becomes Secret Scanning +function getCategory(relativePath: string): string { + const directory = relativePath.split(path.sep)[1] + const category = directory + .split('-') + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(' ') + + // Clarify this one category + return category.replace('Content Render', 'Content Tasks') +} + +function findScriptName(scripts: Record, relativePath: string): string | null { + for (const [scriptName, command] of Object.entries(scripts)) { + // Check if the command includes this file path + if (command.includes(relativePath)) { + return scriptName + } + // Also check for simplified paths without the src/ prefix + const simplifiedPath = relativePath.replace(/^src\//, '') + if (command.includes(simplifiedPath)) { + return scriptName + } + } + return null +} + +function prioritizeOrder(tools: WriterToolsCollection) { + // Define priorities for specific tools + const priorities = { + 'move-content': 1, + 'cta-builder': 2, + 'lint-content': 1, + docstat: 1, + dev: 1, + } + + // Assign priorities to discovered tools + Object.values(tools) + .flat() + .forEach((tool) => { + if (priorities[tool.name as keyof typeof priorities]) { + tool.priority = priorities[tool.name as keyof typeof priorities] + } + }) + + // Sort each category by priority, then alphabetically + Object.keys(tools).forEach((category) => { + tools[category].sort((a, b) => { + // Items with priority come first + if (a.priority !== undefined && b.priority === undefined) return -1 + if (a.priority === undefined && b.priority !== undefined) return 1 + + // Both have priority: sort by priority value + if (a.priority !== undefined && b.priority !== undefined) { + return a.priority - b.priority + } + + // Neither has priority: sort alphabetically + return a.name.localeCompare(b.name) + }) + }) + + return tools +} + +async function main(): Promise { + console.log('For more info, run a command with "-- --help".\n') + + const tools = prioritizeOrder(await discoverWriterTools()) + + Object.entries(tools).forEach(([category, scripts]) => { + console.log(`${category}:`) + scripts.forEach((script) => { + const padding = ' '.repeat(Math.max(0, 34 - script.name.length)) + console.log(` npm run ${script.name}${padding}# ${script.description}`) + }) + console.log('') + }) +} + +if (import.meta.url === `file://${process.argv[1]}`) { + try { + await main() + } catch (error) { + console.error(error) + process.exit(1) + } +}