diff --git a/.github/workflows/nextjs.yml b/.github/workflows/nextjs.yml index 5ba3de25..c5f88400 100644 --- a/.github/workflows/nextjs.yml +++ b/.github/workflows/nextjs.yml @@ -58,7 +58,7 @@ jobs: ${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}- - name: Install dependencies - run: yarn install + run: yarn install --frozen-lockfile - name: Build documentation run: yarn build-pages diff --git a/.github/workflows/test-build-nextjs.yml b/.github/workflows/test-build-nextjs.yml index 4803e768..cbb451da 100644 --- a/.github/workflows/test-build-nextjs.yml +++ b/.github/workflows/test-build-nextjs.yml @@ -55,7 +55,7 @@ jobs: ${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}- - name: Install dependencies - run: yarn install + run: yarn install --frozen-lockfile - name: Build documentation run: yarn build-pages \ No newline at end of file diff --git a/README.md b/README.md index 8d1edd27..59c9993d 100644 --- a/README.md +++ b/README.md @@ -47,10 +47,10 @@ Use Gitpod for a hassle-free cloud-based IDE experience: 1. Clone this GitHub repository. 2. Make sure to have the latest version of [NodeJS LTS](https://nodejs.org/en/download/) installed. 3. Open your terminal in the project directory. -4. Install dependencies: +4. Install dependencies without modifying the `yarn.lock`: ``` - yarn + yarn deps ``` 5. Start your local development server: diff --git a/next.config.js b/next.config.js index 34ee637c..6b4e6f2b 100644 --- a/next.config.js +++ b/next.config.js @@ -61,71 +61,6 @@ export default withNextra({ // defaultLocale: 'default', // // localeDetection: false, // }, - redirects: () => [ - // Language→Guides pages are moved under Book section - { - source: '/language/guides/:page*', - destination: '/book/:page*', - permanent: true, - }, - - // Getting Started is now a part of Book→Guides sub-section - { - source: '/start', - destination: '/book/guides/getting-started', - permanent: true, - }, - { - source: '/start/:slug(first|deploy|test)', - destination: '/book/guides/getting-started/:slug', - permanent: true, - }, - - // Language→Guides→Grammar is now under Language section as a Specification page - { - source: '/book/grammar', - destination: '/language/spec', - permanent: true, - }, - - // Evolution section is moved under Language section - { - source: '/evolution', - destination: '/language/evolution/overview', - permanent: true, - }, - { - source: '/evolution/:page*', - destination: '/language/evolution/:page*', - permanent: true, - }, - - // Language→Changelog is merged with the Evolution page - { - source: '/book/changelog', - destination: '/language/evolution/overview', - permanent: true, - }, - - // Tools are now part of a new Ecosystem section - { - source: '/tools/:page*', - destination: '/ecosystem/tools/:page*', - permanent: true, - }, - - // Small updates in naming of pages or sub-sections - { - source: '/book/message-modes', - destination: '/book/message-mode', - permanent: true, - }, - { - source: '/ecosystem/tools/vs', - destination: '/ecosystem/tools/vscode', - permanent: true, - }, - ], webpack(config) { const allowedSvgRegex = /components\/icons\/.+\.svg$/ diff --git a/package.json b/package.json index 8ac43b65..928b7184 100644 --- a/package.json +++ b/package.json @@ -9,11 +9,12 @@ "react-dom": "^18.2.0" }, "scripts": { - "clean": "rm -fr .next", - "dev": "yarn clean && next", - "build": "yarn clean && next build", + "clean": "rm -fr .next out", + "deps": "yarn install --frozen-lockfile", + "dev": "yarn deps && yarn clean && next", + "build": "yarn deps && yarn clean && next build", "post-build": "echo 'spell checking, link checking, formatting'", - "build-pages": "yarn build && next export", + "build-pages": "yarn build && next export && node ./scripts/redirects-generate.js", "next": "next" }, "devDependencies": { diff --git a/scripts/redirects-generate.js b/scripts/redirects-generate.js new file mode 100644 index 00000000..fc6b87b6 --- /dev/null +++ b/scripts/redirects-generate.js @@ -0,0 +1,211 @@ +// ---- +// TODO: deprecate old links eventually (in 3-6 months from now), and don't create any redirects then. +// ---- + +import fs from 'fs'; +import process from 'process'; +import path from 'path'; + +/* ------------------- */ +/* ---- Functions ---- */ +/* ------------------- */ + +/** + * Constructs an HTML redirect page + * + * @param href {string} + * @returns {string} + */ +const constructRedirectPage = (href) => + ` + + + Redirecting... + + + + + +

Redirecting...

+ + `; + +/** + * Produces an array of redirects to be created, taken from next.config.js property with slight modifications. + * Those redirects are essentially a diff of the current structure and the previous one on this commit: + * https://github.com/tact-lang/tact-docs/tree/17176cb5e8bac84163bfa4c7b1bd94c0dc917bea/pages + * + * @returns {{source: string, subSources: string[] | undefined, destination: string}[]} + */ +const getRedirects = () => [ + // Language→Guides pages are moved under Book section + { + source: '/language/guides', + subSources: [ + 'types', 'functions', 'statements', 'constants', 'receive', 'bounced', 'external', 'lifecycle', 'send', + 'message', 'deploy', 'debug', 'upgrades', 'masterchain', 'func', 'config', 'programmatic', + ], + destination: '/book', + }, + + // Getting Started is now a part of Book→Guides sub-section + { + source: '/start', + subSources: undefined, + destination: '/book/guides/getting-started', + }, + { + source: '/start', + subSources: ['first', 'deploy', 'test'], + destination: '/book/guides/getting-started/:slug', + }, + + // Language→Guides→Grammar is now under Language section as a Specification page + { + source: '/book/grammar', + subSources: undefined, + destination: '/language/spec', + }, + + // Evolution section is moved under Language section + { + source: '/evolution', + subSources: undefined, + destination: '/language/evolution/overview', + }, + { + source: '/evolution', + subSources: ['OTP-001', 'OTP-002', 'OTP-003', 'OTP-004', 'OTP-005', 'OTP-006'], + destination: '/language/evolution', + }, + + // Language→Guides→Changelog is merged with the Evolution page + { + source: '/language/guides/changelog', + subSources: undefined, + destination: '/language/evolution/overview', + }, + + // Tools are now part of a new Ecosystem section + { + source: '/tools', + subSources: ['typescript', 'vs', 'jetbrains'], + destination: '/ecosystem/tools', + }, + + // Small updates in naming of pages or sub-sections + { + source: '/book/message-modes', + subSources: undefined, + destination: '/book/message-mode', + }, + { + source: '/ecosystem/tools/vs', + subSources: undefined, + destination: '/ecosystem/tools/vscode', + }, +]; + +/** + * Checks if the given filepath is a regular page/file and not a redirect page/file + * + * @param filePathWithExt {string} + * @returns bool + */ +const isRegularFile = (filePathWithExt) => { + if (fs.existsSync(filePathWithExt) + && !(fs.readFileSync(filePathWithExt, 'utf8').includes('Redirecting...'))) { + return true; + } + + return false; +}; + +/** + * Creates the redirect file, unless there exists a regular page/file under source path already. + * Returns true if the redirect file was created and false otherwise. + * + * @param source {string} + * @param destination {string} + * @returns bool + */ +const createRedirectFile = (source, destination) => { + // full path, minus the extension + const pathUntilExt = path.join(cwd, '/out', source); + + // full path with extension + const pathWithExt = pathUntilExt + '.html'; + + // if file exists, but doesn't include the line from the redirect page + if (isRegularFile(pathWithExt)) { + console.log(`Warning: such path (${pathWithExt}) already exists in the docs, so redirect from it was NOT created`); + return false; + } + + // need to create additional dirs + if (source.split('/').length > 1) { + const nestedDir = path.dirname(pathUntilExt); + // NOTE: debug output + // console.log(nestedDir, redirect.source); + fs.mkdirSync(nestedDir, { recursive: true }); + } + + // create the redirect file + fs.writeFileSync(pathWithExt, constructRedirectPage(destination)); + + // report success + return true; +}; + +/* ---------------- */ +/* ---- Script ---- */ +/* ---------------- */ + +const cwd = process.cwd(); // current working directory of the process + +// Not in the root dir of the repo +if (!cwd.endsWith('tact-docs')) { + console.log(`Error: not running from the root directory of the repo, but from ${cwd}`); + process.exit(1); +} + +// out/ doesn't exist +if (!fs.existsSync(path.join(cwd, '/out'))) { + console.log(`Error: out/ dir doesn't exist, try building it first`); + process.exit(1); +} + +// get a list of redirects +const redirects = getRedirects(); + +// count expected number of redirects to be created +let expectedCount = 0; +for (const redirect of redirects) { + expectedCount += (redirect.subSources?.length ?? 1); +} + +// log start +console.log(`Started generating redirects. Expected count: ${expectedCount}`); + +// for each redirect create redirect file(s) and/or missing folder(s) +let count = 0; +for (const redirect of redirects) { + // 1. have no nested sub-sources + if (redirect.subSources === undefined) { + if (createRedirectFile(redirect.source, redirect.destination) === true) { + count += 1; + } + continue; + } + + // 2. have some nested structure + for (const subSource of redirect.subSources) { + if (createRedirectFile(redirect.source + '/' + subSource, redirect.destination + '/' + subSource)) { + count += 1; + } + } +} + +// log end +console.log(`Finished generating redirects. Actually generated: ${count}`); \ No newline at end of file