diff --git a/.prettierignore b/.prettierignore index 0e330dab3f9..42666da4b85 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,7 +6,8 @@ coverage **/.next **/.keystone **/__generated__ -docs/ +docs/**/*.md +docs/public/assets/*.js .keystone/tests prisma-utils/src/generated nexus-typegen.ts diff --git a/docs/markdoc/index.ts b/docs/markdoc/index.ts index bf16afe68af..e07886eb65c 100644 --- a/docs/markdoc/index.ts +++ b/docs/markdoc/index.ts @@ -6,6 +6,7 @@ import { isNonEmptyArray } from 'emery/guards'; import { assert } from 'emery/assertions'; import { load } from 'js-yaml'; import { baseMarkdocConfig } from './config'; +import { showNextReleaseWithoutReplacement } from './show-next-release'; export function printValidationError(error: ValidateError) { const location = error.error.location || error.location; @@ -47,7 +48,7 @@ export async function readDocContent(filepath: string): Promise { const markdocConfig: Config = { ...baseMarkdocConfig, variables: { - nextRelease: !!process.env.SHOW_NEXT_RELEASE, + nextRelease: showNextReleaseWithoutReplacement, }, }; diff --git a/docs/markdoc/show-next-release.ts b/docs/markdoc/show-next-release.ts new file mode 100644 index 00000000000..e9d39b324c6 --- /dev/null +++ b/docs/markdoc/show-next-release.ts @@ -0,0 +1,2 @@ +// @skipShowNextReleaseReplacement +export const showNextReleaseWithoutReplacement = !!process.env.SHOW_NEXT_RELEASE; diff --git a/docs/package.json b/docs/package.json index 233fc350ea6..191cbd37182 100644 --- a/docs/package.json +++ b/docs/package.json @@ -9,11 +9,13 @@ "build": "yarn validate-links && next build && yarn sitemap", "start": "next start -p 8000", "sitemap": "next-sitemap", - "validate-links": "tsx validate-links.ts", - "remove-conditionals": "tsx remove-conditionals.ts" + "validate-links": "tsx scripts/validate-links.ts", + "remove-conditionals": "tsx scripts/replace-show-next-release/index.ts" }, "dependencies": { + "@babel/core": "^7.16.0", "@babel/runtime": "^7.16.3", + "@codemod/core": "^2.0.1", "@emotion/cache": "11.10.3", "@emotion/css": "^11.7.1", "@emotion/react": "^11.7.1", @@ -46,6 +48,7 @@ "markdown-it": "^13.0.1", "next": "^12.2.4", "next-compose-plugins": "^2.2.1", + "prettier": "^2.5.0", "prism-react-renderer": "^1.2.1", "react": "^18.1.0", "react-dom": "^18.1.0", diff --git a/docs/remove-conditionals.ts b/docs/scripts/replace-show-next-release/index.ts similarity index 51% rename from docs/remove-conditionals.ts rename to docs/scripts/replace-show-next-release/index.ts index 8b87003c234..adbce13b6e9 100644 --- a/docs/remove-conditionals.ts +++ b/docs/scripts/replace-show-next-release/index.ts @@ -1,10 +1,29 @@ import fs from 'fs/promises'; import { ValidateError } from '@markdoc/markdoc'; -import { loadAllMarkdoc } from './markdoc/load-all'; -import { printValidationError } from './markdoc'; -import { removeNextReleaseConditions } from './markdoc/remove-next-release-conditions'; +import globby from 'globby'; +import { loadAllMarkdoc } from '../../markdoc/load-all'; +import { printValidationError } from '../../markdoc'; +import { removeNextReleaseConditions } from './markdoc'; +import { replaceShowNextRelease } from './typescript'; -(async () => { +async function updateTsFiles() { + const files = await globby('**/*.{ts,tsx}'); + await Promise.all( + files.map(async file => { + const source = await fs.readFile(file, 'utf8'); + if ( + !source.includes('process.env.SHOW_NEXT_RELEASE') || + source.includes('// @skipShowNextReleaseReplacement') + ) { + return; + } + const newSource = await replaceShowNextRelease(file, source); + await fs.writeFile(file, newSource); + }) + ); +} + +async function updateMarkdocFiles() { const docs = await loadAllMarkdoc(); const allErrors: ValidateError[] = []; await Promise.all( @@ -25,4 +44,8 @@ import { removeNextReleaseConditions } from './markdoc/remove-next-release-condi } process.exitCode = 1; } +} + +(async () => { + await Promise.all([updateTsFiles(), updateMarkdocFiles()]); })(); diff --git a/docs/markdoc/remove-next-release-conditions.test.ts b/docs/scripts/replace-show-next-release/markdoc.test.ts similarity index 95% rename from docs/markdoc/remove-next-release-conditions.test.ts rename to docs/scripts/replace-show-next-release/markdoc.test.ts index 43a222f649d..51dace2e151 100644 --- a/docs/markdoc/remove-next-release-conditions.test.ts +++ b/docs/scripts/replace-show-next-release/markdoc.test.ts @@ -1,4 +1,4 @@ -import { removeNextReleaseConditions } from './remove-next-release-conditions'; +import { removeNextReleaseConditions } from './markdoc'; test('removes if', () => { const content = `## Heading 1 diff --git a/docs/markdoc/remove-next-release-conditions.ts b/docs/scripts/replace-show-next-release/markdoc.ts similarity index 91% rename from docs/markdoc/remove-next-release-conditions.ts rename to docs/scripts/replace-show-next-release/markdoc.ts index 5f8a6805442..81e52195db2 100644 --- a/docs/markdoc/remove-next-release-conditions.ts +++ b/docs/scripts/replace-show-next-release/markdoc.ts @@ -1,5 +1,5 @@ import Markdoc from '@markdoc/markdoc'; -import { baseMarkdocConfig } from './config'; +import { baseMarkdocConfig } from '../../markdoc/config'; const pattern = /{%\s+if\s+\$nextRelease\s+%}\s*([^]+?)\s*(?:{%\s+else\s+\/%}[^]*?)?{%\s+\/if\s+%}/g; diff --git a/docs/scripts/replace-show-next-release/typescript.test.ts b/docs/scripts/replace-show-next-release/typescript.test.ts new file mode 100644 index 00000000000..3304f0d6594 --- /dev/null +++ b/docs/scripts/replace-show-next-release/typescript.test.ts @@ -0,0 +1,112 @@ +import { replaceShowNextRelease } from './typescript'; +test('no usage', async () => { + expect(await replaceShowNextRelease('a.ts', "const a = 'something';")).toMatchInlineSnapshot(` + "const a = 'something'; + " + `); +}); +test('assignment to variable', async () => { + expect(await replaceShowNextRelease('a.ts', 'const a = process.env.SHOW_NEXT_RELEASE;')) + .toMatchInlineSnapshot(` + "const a = '1'; + " + `); +}); +test('basic if statement', async () => { + expect( + await replaceShowNextRelease( + 'a.ts', + ` +function a() { + if (process.env.SHOW_NEXT_RELEASE) { + console.log('yes') + } else { + console.log('other') + } +} +` + ) + ).toMatchInlineSnapshot(` + "function a() { + console.log('yes'); + } + " + `); +}); +test('conditional expression', async () => { + expect( + await replaceShowNextRelease( + 'a.ts', + ` +function a() { + const a = process.env.SHOW_NEXT_RELEASE ? 'new release' : 'old'; +} +` + ) + ).toMatchInlineSnapshot(` + "function a() { + const a = 'new release'; + } + " + `); +}); +test('conditional expression in jsx', async () => { + expect( + await replaceShowNextRelease( + 'a.tsx', + ` +function MyThing() { + return
something {process.env.SHOW_NEXT_RELEASE ? 'next release' : 'previous release'} other
+} +` + ) + ).toMatchInlineSnapshot(` + "function MyThing() { + return
something {'next release'} other
; + } + " + `); +}); +test('conditional expression in jsx with jsx in consequent', async () => { + expect( + await replaceShowNextRelease( + 'a.tsx', + ` +function MyThing() { + return
something {process.env.SHOW_NEXT_RELEASE ?
new release
: 'previous release'} other
+} +` + ) + ).toMatchInlineSnapshot(` + "function MyThing() { + return ( +
+ something
new release
other +
+ ); + } + " + `); +}); + +test('&&', async () => { + expect( + await replaceShowNextRelease( + 'a.tsx', + ` +function MyThing() { + return
something {process.env.SHOW_NEXT_RELEASE &&
new release
} other
+} +` + ) + ).toMatchInlineSnapshot(` + "function MyThing() { + return ( +
+ something
new release
other +
+ ); + } + " + `); +}); diff --git a/docs/scripts/replace-show-next-release/typescript.ts b/docs/scripts/replace-show-next-release/typescript.ts new file mode 100644 index 00000000000..f667035ef00 --- /dev/null +++ b/docs/scripts/replace-show-next-release/typescript.ts @@ -0,0 +1,75 @@ +// @codemod/core is basically @babel/core but it uses recast to preserve formatting/comments better +import { transform } from '@codemod/core'; +import { NodePath, Node, types } from '@babel/core'; +import prettier from 'prettier'; + +function truthyConditionalExpression(path: NodePath) { + if (path.parentPath?.isConditionalExpression() && path.parentKey === 'test') { + return { consequent: path.parentPath.node.consequent, path: path.parentPath }; + } + if ( + path.parentPath?.isLogicalExpression() && + path.parentKey === 'left' && + path.parentPath.node.operator === '&&' + ) { + return { consequent: path.parentPath.node.right, path: path.parentPath }; + } +} + +export async function replaceShowNextRelease(filename: string, source: string) { + const transformed = transform(source, { + parserOpts: { plugins: filename.endsWith('.tsx') ? ['typescript', 'jsx'] : ['typescript'] }, + plugins: [ + { + visitor: { + MemberExpression(path) { + const { node } = path; + if ( + !( + !node.computed && + node.property.type === 'Identifier' && + node.property.name === 'SHOW_NEXT_RELEASE' && + node.object.type === 'MemberExpression' && + !node.object.computed && + node.object.property.type === 'Identifier' && + node.object.property.name === 'env' && + node.object.object.type === 'Identifier' && + node.object.object.name === 'process' + ) + ) { + return; + } + if ( + path.parentPath.isIfStatement() && + path.parentKey === 'test' && + path.parentPath.node.consequent.type === 'BlockStatement' + ) { + path.parentPath.replaceWithMultiple(path.parentPath.node.consequent.body); + return; + } + + const conditional = truthyConditionalExpression(path); + if (conditional) { + if ( + conditional.path.parentPath?.isJSXExpressionContainer() && + conditional.consequent.type === 'JSXElement' + ) { + conditional.path.parentPath.replaceWith(conditional.consequent); + return; + } + conditional.path.replaceWith(conditional.consequent); + return; + } + path.replaceWith(types.stringLiteral('1')); + }, + }, + }, + ], + babelrc: false, + configFile: false, + babelrcRoots: false, + filename, + }); + const config = await prettier.resolveConfig(filename); + return prettier.format(transformed!.code!, { filepath: filename, ...config }); +} diff --git a/docs/validate-links.ts b/docs/scripts/validate-links.ts similarity index 87% rename from docs/validate-links.ts rename to docs/scripts/validate-links.ts index ce0a286f429..9a9f18c923c 100644 --- a/docs/validate-links.ts +++ b/docs/scripts/validate-links.ts @@ -1,7 +1,7 @@ import Markdoc from '@markdoc/markdoc'; -import { getIdForHeading, baseMarkdocConfig, Pages } from './markdoc/config'; -import { printValidationError } from './markdoc'; -import { loadAllMarkdoc } from './markdoc/load-all'; +import { getIdForHeading, baseMarkdocConfig, Pages } from '../markdoc/config'; +import { printValidationError } from '../markdoc'; +import { loadAllMarkdoc } from '../markdoc/load-all'; // for the things that aren't Markdoc that are linked from Markdoc const NON_MARKDOWN_PAGES = [ diff --git a/yarn.lock b/yarn.lock index a5057f9443f..6dbdc0abbf0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1258,6 +1258,15 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.16.0": + version "7.19.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.5.tgz#da3f4b301c8086717eee9cab14da91b1fa5dcca7" + integrity sha512-DxbNz9Lz4aMZ99qPpO1raTbcrI1ZeYh+9NR9qhfkQIbFtVEqotHojEBxHzmxhVONkGt6VyrqVQcgpefMy9pqcg== + dependencies: + "@babel/types" "^7.19.4" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -1430,7 +1439,12 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== -"@babel/helper-validator-identifier@^7.18.6": +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== @@ -1473,6 +1487,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.1.tgz#6f6d6c2e621aad19a92544cc217ed13f1aac5b4c" integrity sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A== +"@babel/parser@^7.16.3": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.4.tgz#03c4339d2b8971eb3beca5252bafd9b9f79db3dc" + integrity sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" @@ -2227,6 +2246,15 @@ "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" +"@babel/types@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.4.tgz#0dd5c91c573a202d600490a35b33246fed8a41c7" + integrity sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -2458,6 +2486,25 @@ human-id "^1.0.2" prettier "^2.7.1" +"@codemod/core@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@codemod/core/-/core-2.0.1.tgz#6925a6de3af88fcf492dadf44cf8944f9e67c0bc" + integrity sha512-2h/CDscD6jFJujwZJQ9e8i3w+Er2uyiJk0VQxhZoXXxlIu5w4FHbFpqyUV3zb5aprml7gCUBTyNWRZtccB9FvA== + dependencies: + "@babel/core" "^7.16.0" + "@babel/generator" "^7.16.0" + "@codemod/parser" "^1.2.1" + is-ci-cli "^2.2.0" + recast "^0.19.0" + resolve "^1.12.0" + +"@codemod/parser@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@codemod/parser/-/parser-1.2.1.tgz#255aa050bf8174c2c091ac9fcf2d45803932db50" + integrity sha512-JJKEESRXSsfbvrKnXaxaOLxrBeCrv0lCEin3MeSepbwQmKb138eGpK22c+M6/mQUuAFYbsGdtJ5eI/mdf4rXww== + dependencies: + "@babel/parser" "^7.16.3" + "@corex/deepmerge@^4.0.29": version "4.0.29" resolved "https://registry.yarnpkg.com/@corex/deepmerge/-/deepmerge-4.0.29.tgz#af9debf07d7f6b0d2a9d04a266abf2c1418ed2f6" @@ -5186,6 +5233,11 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== +ast-types@0.13.3: + version "0.13.3" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.3.tgz#50da3f28d17bdbc7969a3a2d83a0e4a72ae755a7" + integrity sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA== + ast-types@^0.13.2: version "0.13.4" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" @@ -6229,7 +6281,7 @@ cross-fetch@^3.1.4, cross-fetch@^3.1.5: dependencies: node-fetch "2.6.7" -cross-spawn@7.0.3, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -7214,7 +7266,7 @@ espree@^9.4.0: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -8688,6 +8740,14 @@ is-callable@^1.1.4, is-callable@^1.2.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.6.tgz#fd6170b0b8c7e2cc73de342ef8284a2202023c44" integrity sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q== +is-ci-cli@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-ci-cli/-/is-ci-cli-2.2.0.tgz#2cf6763f98413ff8a8409b400f85ac063e0c8a44" + integrity sha512-Xg97ZGDzU0a9gPTAli+TNegMk+PI3x0KLRYCfBa2LAboF1YyuA03Gwdc9vpu3VRNU+lFFNkvXnIQuJ0PgB120Q== + dependencies: + cross-spawn "^7.0.0" + is-ci "^2.0.0" + is-ci@3.0.1, is-ci@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" @@ -11859,6 +11919,11 @@ prisma@4.3.1: dependencies: "@prisma/engines" "4.3.1" +private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -12249,6 +12314,16 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +recast@^0.19.0: + version "0.19.1" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.19.1.tgz#555f3612a5a10c9f44b9a923875c51ff775de6c8" + integrity sha512-8FCjrBxjeEU2O6I+2hyHyBFH1siJbMBLwIRvVr1T3FD2cL754sOaJDsJ/8h3xYltasbJ8jqWRIhMuDGBSiSbjw== + dependencies: + ast-types "0.13.3" + esprima "~4.0.0" + private "^0.1.8" + source-map "~0.6.1" + redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"