From f18ca9cc2b208770e0e8beb58579dfe4ec97077c Mon Sep 17 00:00:00 2001 From: Elliot DeNolf Date: Mon, 20 Jan 2025 11:34:51 -0500 Subject: [PATCH] build: move larger scripts into tools dir in workspace (#10653) Having the `scripts` dir re-use all packages from the top-level was getting quite unwieldy. Created new `tools` directory that is part of the workspace. Packages are exported with the `@tools` package namespace. --- .github/workflows/main.yml | 4 +- eslint.config.js | 8 ++ package.json | 17 ++- pnpm-lock.yaml | 119 ++++++++++++---- pnpm-workspace.yaml | 1 + scripts/publish-canary.ts | 58 -------- test/dev.ts | 6 +- test/jest.setup.js | 1 + test/package.json | 2 + tools/constants/package.json | 17 +++ tools/constants/src/index.ts | 13 ++ tools/constants/tsconfig.json | 6 + tools/releaser/package.json | 53 +++++++ tools/releaser/src/index.ts | 2 + .../releaser/src}/lib/getPackageDetails.ts | 21 ++- .../src}/lib/getPackageRegistryVersions.ts | 0 .../releaser/src}/lib/getWorkspace.ts | 6 + .../releaser/src}/lib/publishList.ts | 0 tools/releaser/src/publish-canary.ts | 31 +++++ {scripts => tools/releaser/src}/release.ts | 52 ++++--- .../src}/utils/createDraftGitHubRelease.ts | 0 .../src}/utils/generateReleaseNotes.ts | 77 +++++----- .../releaser/src}/utils/getLatestCommits.ts | 0 .../releaser/src}/utils/getRecommendedBump.ts | 0 tools/releaser/tsconfig.json | 7 + tools/scripts/package.json | 34 +++++ .../src}/build-template-with-local-pkgs.ts | 11 +- .../src}/generate-template-variations.ts | 131 +++++++++--------- tools/scripts/src/index.ts | 0 .../scripts/src}/pack-all-to-dest.ts | 26 ++-- tools/scripts/tsconfig.json | 6 + tsconfig.base.json | 1 - 32 files changed, 448 insertions(+), 262 deletions(-) delete mode 100755 scripts/publish-canary.ts create mode 100644 tools/constants/package.json create mode 100644 tools/constants/src/index.ts create mode 100644 tools/constants/tsconfig.json create mode 100644 tools/releaser/package.json create mode 100644 tools/releaser/src/index.ts rename {scripts => tools/releaser/src}/lib/getPackageDetails.ts (74%) rename {scripts => tools/releaser/src}/lib/getPackageRegistryVersions.ts (100%) rename {scripts => tools/releaser/src}/lib/getWorkspace.ts (97%) rename {scripts => tools/releaser/src}/lib/publishList.ts (100%) create mode 100755 tools/releaser/src/publish-canary.ts rename {scripts => tools/releaser/src}/release.ts (89%) rename {scripts => tools/releaser/src}/utils/createDraftGitHubRelease.ts (100%) rename {scripts => tools/releaser/src}/utils/generateReleaseNotes.ts (91%) rename {scripts => tools/releaser/src}/utils/getLatestCommits.ts (100%) rename {scripts => tools/releaser/src}/utils/getRecommendedBump.ts (100%) create mode 100644 tools/releaser/tsconfig.json create mode 100644 tools/scripts/package.json rename {scripts => tools/scripts/src}/build-template-with-local-pkgs.ts (90%) rename {scripts => tools/scripts/src}/generate-template-variations.ts (94%) create mode 100644 tools/scripts/src/index.ts rename {scripts => tools/scripts/src}/pack-all-to-dest.ts (75%) create mode 100644 tools/scripts/tsconfig.json diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a248a4ce6..0f7d1bdd7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -457,7 +457,9 @@ jobs: - name: Build Template run: | pnpm run script:pack --dest templates/${{ matrix.template }} - pnpm runts scripts/build-template-with-local-pkgs.ts ${{ matrix.template }} $POSTGRES_URL + pnpm run script:build-template-with-local-pkgs ${{ matrix.template }} $POSTGRES_URL + env: + NODE_OPTIONS: --max-old-space-size=8096 tests-type-generation: runs-on: ubuntu-24.04 diff --git a/eslint.config.js b/eslint.config.js index bc4e93563..efe135f95 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -68,6 +68,14 @@ export const rootEslintConfig = [ 'perfectionist/sort-objects': 'off', }, }, + { + files: ['tools/**/*.ts'], + rules: { + 'no-console': 'off', + 'perfectionist/sort-object-types': 'off', + 'perfectionist/sort-objects': 'off', + }, + }, ] export default [ diff --git a/package.json b/package.json index 3b8fa52dd..5119431c9 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "build:live-preview-react": "turbo build --filter \"@payloadcms/live-preview-react\"", "build:live-preview-vue": "turbo build --filter \"@payloadcms/live-preview-vue\"", "build:next": "turbo build --filter \"@payloadcms/next\"", + "build:packages": "turbo build --filter=./packages/*", "build:payload": "turbo build --filter payload", "build:payload-cloud": "turbo build --filter \"@payloadcms/payload-cloud\"", "build:plugin-cloud-storage": "turbo build --filter \"@payloadcms/plugin-cloud-storage\"", @@ -40,6 +41,7 @@ "build:plugin-seo": "turbo build --filter \"@payloadcms/plugin-seo\"", "build:plugin-stripe": "turbo build --filter \"@payloadcms/plugin-stripe\"", "build:plugins": "turbo build --filter \"@payloadcms/plugin-*\"", + "build:releaser": "turbo build --filter \"@tools/releaser\"", "build:richtext-lexical": "turbo build --filter \"@payloadcms/richtext-lexical\"", "build:richtext-slate": "turbo build --filter \"@payloadcms/richtext-slate\"", "build:storage-azure": "turbo build --filter \"@payloadcms/storage-azure\"", @@ -48,6 +50,7 @@ "build:storage-uploadthing": "turbo build --filter \"@payloadcms/storage-uploadthing\"", "build:storage-vercel-blob": "turbo build --filter \"@payloadcms/storage-vercel-blob\"", "build:tests": "pnpm --filter payload-test-suite run typecheck", + "build:tools": "turbo build --filter=./tools/*", "build:translations": "turbo build --filter \"@payloadcms/translations\"", "build:ui": "turbo build --filter \"@payloadcms/ui\"", "clean": "turbo clean", @@ -77,12 +80,13 @@ "prepare-run-test-against-prod": "pnpm bf && rm -rf test/packed && rm -rf test/node_modules && rm -rf app && rm -f test/pnpm-lock.yaml && pnpm run script:pack --all --no-build --dest test/packed && pnpm runts test/setupProd.ts && cd test && pnpm i --ignore-workspace && cd ..", "prepare-run-test-against-prod:ci": "rm -rf test/node_modules && rm -rf app && rm -f test/pnpm-lock.yaml && pnpm run script:pack --all --no-build --dest test/packed && pnpm runts test/setupProd.ts && cd test && pnpm i --ignore-workspace && cd ..", "reinstall": "pnpm clean:all && pnpm install", - "release": "pnpm runts ./scripts/release.ts --tag latest", + "release": "pnpm --filter releaser release --tag latest", "release:beta": "pnpm runts ./scripts/release.ts --bump prerelease --tag beta", "runts": "cross-env NODE_OPTIONS=--no-deprecation node --no-deprecation --import @swc-node/register/esm-register", - "script:gen-templates": "pnpm runts ./scripts/generate-template-variations.ts", - "script:list-published": "pnpm runts scripts/lib/getPackageRegistryVersions.ts", - "script:pack": "pnpm runts scripts/pack-all-to-dest.ts", + "script:build-template-with-local-pkgs": "pnpm --filter scripts build-template-with-local-pkgs", + "script:gen-templates": "pnpm --filter scripts gen-templates", + "script:list-published": "pnpm --filter releaser list-published", + "script:pack": "pnpm --filter scripts pack-all-to-dest", "pretest": "pnpm build", "test": "pnpm test:int && pnpm test:components && pnpm test:e2e", "test:components": "cross-env NODE_OPTIONS=\" --no-deprecation\" jest --config=jest.components.config.js", @@ -127,13 +131,10 @@ "@types/jest": "29.5.12", "@types/minimist": "1.2.5", "@types/node": "22.5.4", - "@types/prompts": "^2.4.5", "@types/react": "19.0.1", "@types/react-dom": "19.0.1", - "@types/semver": "^7.5.3", "@types/shelljs": "0.8.15", "chalk": "^4.1.2", - "changelogen": "^0.5.5", "comment-json": "^4.2.3", "copyfiles": "2.4.1", "create-payload-app": "workspace:*", @@ -157,11 +158,9 @@ "playwright": "1.49.1", "playwright-core": "1.49.1", "prettier": "3.3.3", - "prompts": "2.4.2", "react": "19.0.0", "react-dom": "19.0.0", "rimraf": "6.0.1", - "semver": "^7.5.4", "sharp": "0.32.6", "shelljs": "0.8.5", "slash": "3.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3516b6806..5c1be2fa2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,7 +45,7 @@ importers: version: 1.49.1 '@sentry/nextjs': specifier: ^8.33.1 - version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.9.3(@swc/helpers@0.5.15))) + version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.9.3(@swc/helpers@0.5.15))) '@sentry/node': specifier: ^8.33.1 version: 8.37.1 @@ -70,27 +70,18 @@ importers: '@types/node': specifier: 22.5.4 version: 22.5.4 - '@types/prompts': - specifier: ^2.4.5 - version: 2.4.9 '@types/react': specifier: 19.0.1 version: 19.0.1 '@types/react-dom': specifier: 19.0.1 version: 19.0.1 - '@types/semver': - specifier: ^7.5.3 - version: 7.5.8 '@types/shelljs': specifier: 0.8.15 version: 0.8.15 chalk: specifier: ^4.1.2 version: 4.1.2 - changelogen: - specifier: ^0.5.5 - version: 0.5.7 comment-json: specifier: ^4.2.3 version: 4.2.5 @@ -144,7 +135,7 @@ importers: version: 10.1.3(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3) next: specifier: 15.1.5 - version: 15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4) + version: 15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4) open: specifier: ^10.1.0 version: 10.1.0 @@ -160,9 +151,6 @@ importers: prettier: specifier: 3.3.3 version: 3.3.3 - prompts: - specifier: 2.4.2 - version: 2.4.2 react: specifier: 19.0.0 version: 19.0.0 @@ -172,9 +160,6 @@ importers: rimraf: specifier: 6.0.1 version: 6.0.1 - semver: - specifier: ^7.5.4 - version: 7.6.3 sharp: specifier: 0.32.6 version: 0.32.6 @@ -1020,7 +1005,7 @@ importers: dependencies: next: specifier: ^15.0.3 - version: 15.1.3(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4) + version: 15.1.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4) devDependencies: '@payloadcms/eslint-config': specifier: workspace:* @@ -1082,7 +1067,7 @@ importers: dependencies: '@sentry/nextjs': specifier: ^8.33.1 - version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.9.3(@swc/helpers@0.5.15))) + version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.9.3(@swc/helpers@0.5.15))) '@sentry/types': specifier: ^8.33.1 version: 8.37.1 @@ -1432,7 +1417,7 @@ importers: version: link:../plugin-cloud-storage uploadthing: specifier: 7.3.0 - version: 7.3.0(next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)) + version: 7.3.0(next@15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)) devDependencies: payload: specifier: workspace:* @@ -1712,10 +1697,13 @@ importers: version: link:../packages/ui '@sentry/nextjs': specifier: ^8.33.1 - version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.9.3(@swc/helpers@0.5.15))) + version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.9.3(@swc/helpers@0.5.15))) '@sentry/react': specifier: ^7.77.0 version: 7.119.2(react@19.0.0) + '@types/jest': + specifier: 29.5.12 + version: 29.5.12 '@types/react': specifier: 19.0.1 version: 19.0.1 @@ -1749,6 +1737,9 @@ importers: http-status: specifier: 1.6.2 version: 1.6.2 + jest: + specifier: 29.7.0 + version: 29.7.0(@types/node@22.5.4)(babel-plugin-macros@3.1.0) jwt-decode: specifier: 4.0.0 version: 4.0.0 @@ -1757,7 +1748,7 @@ importers: version: 8.9.5(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3) next: specifier: 15.1.5 - version: 15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4) + version: 15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4) nodemailer: specifier: 6.9.16 version: 6.9.16 @@ -1792,6 +1783,78 @@ importers: specifier: 10.0.0 version: 10.0.0 + tools/constants: {} + + tools/releaser: + dependencies: + '@swc-node/register': + specifier: 1.10.9 + version: 1.10.9(@swc/core@1.9.3(@swc/helpers@0.5.15))(@swc/types@0.1.17)(typescript@5.7.3) + '@tools/constants': + specifier: workspace:* + version: link:../constants + chalk: + specifier: ^4.1.2 + version: 4.1.2 + changelogen: + specifier: ^0.5.5 + version: 0.5.7 + execa: + specifier: 5.1.1 + version: 5.1.1 + minimist: + specifier: 1.2.8 + version: 1.2.8 + open: + specifier: ^10.1.0 + version: 10.1.0 + p-limit: + specifier: ^5.0.0 + version: 5.0.0 + prompts: + specifier: 2.4.2 + version: 2.4.2 + semver: + specifier: ^7.5.4 + version: 7.6.3 + tsx: + specifier: ^4.19.2 + version: 4.19.2 + devDependencies: + '@types/minimist': + specifier: 1.2.5 + version: 1.2.5 + '@types/prompts': + specifier: ^2.4.5 + version: 2.4.9 + '@types/semver': + specifier: ^7.5.3 + version: 7.5.8 + + tools/scripts: + dependencies: + '@swc-node/register': + specifier: 1.10.9 + version: 1.10.9(@swc/core@1.9.3(@swc/helpers@0.5.15))(@swc/types@0.1.17)(typescript@5.7.3) + '@tools/constants': + specifier: workspace:* + version: link:../constants + '@tools/releaser': + specifier: workspace:* + version: link:../releaser + chalk: + specifier: ^4.1.2 + version: 4.1.2 + changelogen: + specifier: ^0.5.5 + version: 0.5.7 + create-payload-app: + specifier: workspace:* + version: link:../../packages/create-payload-app + open: + specifier: ^10.1.0 + version: 10.1.0 + packages: '@ampproject/remapping@2.3.0': @@ -13458,7 +13521,7 @@ snapshots: '@sentry/utils': 7.119.2 localforage: 1.10.0 - '@sentry/nextjs@8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.9.3(@swc/helpers@0.5.15)))': + '@sentry/nextjs@8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.9.3(@swc/helpers@0.5.15)))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation-http': 0.53.0(@opentelemetry/api@1.9.0) @@ -13474,7 +13537,7 @@ snapshots: '@sentry/vercel-edge': 8.37.1 '@sentry/webpack-plugin': 2.22.6(webpack@5.96.1(@swc/core@1.9.3(@swc/helpers@0.5.15))) chalk: 3.0.0 - next: 15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4) + next: 15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4) resolve: 1.22.8 rollup: 3.29.5 stacktrace-parser: 0.1.10 @@ -17973,7 +18036,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.1.3(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4): + next@15.1.3(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4): dependencies: '@next/env': 15.1.3 '@swc/counter': 0.1.3 @@ -18001,7 +18064,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4): + next@15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4): dependencies: '@next/env': 15.1.5 '@swc/counter': 0.1.3 @@ -19667,14 +19730,14 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - uploadthing@7.3.0(next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)): + uploadthing@7.3.0(next@15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)): dependencies: '@effect/platform': 0.69.8(effect@3.10.3) '@uploadthing/mime-types': 0.3.2 '@uploadthing/shared': 7.1.1 effect: 3.10.3 optionalDependencies: - next: 15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4) + next: 15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-df7b47d-20241124)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4) uri-js@4.4.1: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 285dddccd..77248f552 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,7 @@ packages: # all packages in direct subdirs of packages/ - 'packages/*' + - 'tools/*' - 'test' # exclude packages that are inside test directories # - '!**/test/**' diff --git a/scripts/publish-canary.ts b/scripts/publish-canary.ts deleted file mode 100755 index 2dd13e643..000000000 --- a/scripts/publish-canary.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { ExecSyncOptions } from 'child_process' -import type execa from 'execa' - -import chalk from 'chalk' -import minimist from 'minimist' -import { fileURLToPath } from 'node:url' -import pLimit from 'p-limit' -import path from 'path' - -import { getWorkspace } from './lib/getWorkspace.js' - -const npmPublishLimit = pLimit(5) - -const filename = fileURLToPath(import.meta.url) -const dirname = path.dirname(filename) -const cwd = path.resolve(dirname, '..') - -const execOpts: ExecSyncOptions = { stdio: 'inherit' } -const execaOpts: execa.Options = { stdio: 'inherit' } - -const args = minimist(process.argv.slice(2)) - -// const { -// bump = 'patch', // Semver release type -// changelog = false, // Whether to update the changelog. WARNING: This gets throttled on too many commits -// 'dry-run': dryRun, -// 'git-tag': gitTag = true, // Whether to run git tag and commit operations -// 'git-commit': gitCommit = true, // Whether to run git commit operations -// tag = 'latest', -// } = args - -const dryRun = true - -async function main() { - const workspace = await getWorkspace() - await workspace.bumpVersion('canary') - await workspace.build() - await workspace.publishSync({ dryRun: false, tag: 'canary' }) - - header('๐ŸŽ‰ Done!') -} - -main().catch((error) => { - console.error(error) - process.exit(1) -}) - -function abort(message = 'Abort', exitCode = 1) { - console.error(chalk.bold.red(`\n${message}\n`)) - process.exit(exitCode) -} - -function header(message: string, opts?: { enable?: boolean }) { - const { enable } = opts ?? {} - if (!enable) return - - console.log(chalk.bold.green(`${message}\n`)) -} diff --git a/test/dev.ts b/test/dev.ts index 9dec3d730..c2b15d136 100644 --- a/test/dev.ts +++ b/test/dev.ts @@ -80,13 +80,13 @@ const app = nextImport({ const handle = app.getRequestHandler() -let resolveServer +let resolveServer: () => void -const serverPromise = new Promise((res) => (resolveServer = res)) +const serverPromise = new Promise((res) => (resolveServer = res)) void app.prepare().then(() => { createServer(async (req, res) => { - const parsedUrl = parse(req.url, true) + const parsedUrl = parse(req.url || '', true) await handle(req, res, parsedUrl) }).listen(port, () => { resolveServer() diff --git a/test/jest.setup.js b/test/jest.setup.js index 1b5d4da0f..6668ca592 100644 --- a/test/jest.setup.js +++ b/test/jest.setup.js @@ -1,3 +1,4 @@ +import { jest } from '@jest/globals' import console from 'console' global.console = console diff --git a/test/package.json b/test/package.json index 8a1ab74eb..3779020b0 100644 --- a/test/package.json +++ b/test/package.json @@ -58,6 +58,7 @@ "@payloadcms/ui": "workspace:*", "@sentry/nextjs": "^8.33.1", "@sentry/react": "^7.77.0", + "@types/jest": "29.5.12", "@types/react": "19.0.1", "@types/react-dom": "19.0.1", "babel-plugin-react-compiler": "19.0.0-beta-df7b47d-20241124", @@ -69,6 +70,7 @@ "execa": "5.1.1", "file-type": "19.3.0", "http-status": "1.6.2", + "jest": "29.7.0", "jwt-decode": "4.0.0", "mongoose": "8.9.5", "next": "15.1.5", diff --git a/tools/constants/package.json b/tools/constants/package.json new file mode 100644 index 000000000..db3df4622 --- /dev/null +++ b/tools/constants/package.json @@ -0,0 +1,17 @@ +{ + "name": "@tools/constants", + "version": "0.0.1", + "description": "", + "keywords": [], + "license": "ISC", + "author": "", + "type": "module", + "exports": { + ".": { + "import": "./src/index.ts" + } + }, + "scripts": { + "build": "tsc" + } +} diff --git a/tools/constants/src/index.ts b/tools/constants/src/index.ts new file mode 100644 index 000000000..3edcf3c51 --- /dev/null +++ b/tools/constants/src/index.ts @@ -0,0 +1,13 @@ +import { fileURLToPath } from 'node:url' +import path from 'path' + +const filename = fileURLToPath(import.meta.url) +const dirname = path.dirname(filename) + +/** + * Path to the project root + */ +export const PROJECT_ROOT = path.resolve(dirname, '../../../') +export const ROOT_PACKAGE_JSON = path.resolve(PROJECT_ROOT, 'package.json') +export const PACKAGES_DIR = path.resolve(PROJECT_ROOT, 'packages') +export const TEMPLATES_DIR = path.resolve(PROJECT_ROOT, 'templates') diff --git a/tools/constants/tsconfig.json b/tools/constants/tsconfig.json new file mode 100644 index 000000000..f9f8fe292 --- /dev/null +++ b/tools/constants/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "strict": true, + } +} diff --git a/tools/releaser/package.json b/tools/releaser/package.json new file mode 100644 index 000000000..4433a2127 --- /dev/null +++ b/tools/releaser/package.json @@ -0,0 +1,53 @@ +{ + "name": "@tools/releaser", + "version": "0.0.1", + "description": "", + "keywords": [], + "license": "ISC", + "author": "", + "type": "module", + "exports": { + ".": { + "import": "./src/index.ts", + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./lib": { + "import": "./src/lib/*.ts", + "types": "./src/lib/*.ts", + "default": "./src/lib/*.ts" + }, + "./utils": { + "import": "./src/utils/*.ts", + "types": "./src/utils/*.ts", + "default": "./src/utils/*.ts" + } + }, + "main": "src/index.ts", + "scripts": { + "build": "tsc", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "list-published": "tsx src/lib/getPackageRegistryVersions.ts", + "release": "tsx src/release.ts", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "@swc-node/register": "1.10.9", + "@tools/constants": "workspace:*", + "chalk": "^4.1.2", + "changelogen": "^0.5.5", + "execa": "5.1.1", + "minimist": "1.2.8", + "open": "^10.1.0", + "p-limit": "^5.0.0", + "prompts": "2.4.2", + "semver": "^7.5.4", + "tsx": "^4.19.2" + }, + "devDependencies": { + "@types/minimist": "1.2.5", + "@types/prompts": "^2.4.5", + "@types/semver": "^7.5.3" + } +} diff --git a/tools/releaser/src/index.ts b/tools/releaser/src/index.ts new file mode 100644 index 000000000..2c7233b56 --- /dev/null +++ b/tools/releaser/src/index.ts @@ -0,0 +1,2 @@ +export { getPackageDetails } from './lib/getPackageDetails.js' +export type { PackageDetails } from './lib/getPackageDetails.js' diff --git a/scripts/lib/getPackageDetails.ts b/tools/releaser/src/lib/getPackageDetails.ts similarity index 74% rename from scripts/lib/getPackageDetails.ts rename to tools/releaser/src/lib/getPackageDetails.ts index 971fa0380..244e1886f 100644 --- a/scripts/lib/getPackageDetails.ts +++ b/tools/releaser/src/lib/getPackageDetails.ts @@ -1,12 +1,7 @@ +import { PROJECT_ROOT } from '@tools/constants' import fse from 'fs-extra' import globby from 'globby' import path, { dirname } from 'path' -import { fileURLToPath } from 'url' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -const projectRoot = path.resolve(__dirname, '../../') export type PackageDetails = { /** Name in package.json / npm registry */ @@ -22,10 +17,10 @@ export type PackageDetails = { /** * Accepts package whitelist (directory names inside packages dir) and returns details for each package */ -export const getPackageDetails = async (packages: string[]): Promise => { +export const getPackageDetails = async (packages?: null | string[]): Promise => { // Fetch all package.json files, filter out packages not in the whitelist const packageJsons = await globby('packages/*/package.json', { - cwd: projectRoot, + cwd: PROJECT_ROOT, absolute: true, }) @@ -33,16 +28,20 @@ export const getPackageDetails = async (packages: string[]): Promise { const packageJson = await fse.readJson(packageJsonPath) const isPublic = packageJson.private !== true - if (!isPublic) return null + if (!isPublic) { + return null + } const isInWhitelist = packages ? packages.includes(path.basename(path.dirname(packageJsonPath))) : true - if (!isInWhitelist) return null + if (!isInWhitelist) { + return null + } return { name: packageJson.name as string, - packagePath: path.relative(projectRoot, dirname(packageJsonPath)), + packagePath: path.relative(PROJECT_ROOT, dirname(packageJsonPath)), shortName: path.dirname(packageJsonPath), version: packageJson.version, } as PackageDetails diff --git a/scripts/lib/getPackageRegistryVersions.ts b/tools/releaser/src/lib/getPackageRegistryVersions.ts similarity index 100% rename from scripts/lib/getPackageRegistryVersions.ts rename to tools/releaser/src/lib/getPackageRegistryVersions.ts diff --git a/scripts/lib/getWorkspace.ts b/tools/releaser/src/lib/getWorkspace.ts similarity index 97% rename from scripts/lib/getWorkspace.ts rename to tools/releaser/src/lib/getWorkspace.ts index d50fb8aff..a503d6963 100644 --- a/scripts/lib/getWorkspace.ts +++ b/tools/releaser/src/lib/getWorkspace.ts @@ -150,6 +150,12 @@ export const getWorkspace = async () => { nextReleaseVersion = semver.inc(monorepoVersion, bumpType) } + if (!nextReleaseVersion) { + throw new Error( + `Invalid bump type: ${bumpType}. Could not determine next version from ${monorepoVersion}.`, + ) + } + console.log(`\n Version: ${monorepoVersion} => ${nextReleaseVersion}\n`) console.log(` Bump: ${bumpType}`) console.log(` Changes (${packageDetails.length} packages):\n`) diff --git a/scripts/lib/publishList.ts b/tools/releaser/src/lib/publishList.ts similarity index 100% rename from scripts/lib/publishList.ts rename to tools/releaser/src/lib/publishList.ts diff --git a/tools/releaser/src/publish-canary.ts b/tools/releaser/src/publish-canary.ts new file mode 100755 index 000000000..108b4809a --- /dev/null +++ b/tools/releaser/src/publish-canary.ts @@ -0,0 +1,31 @@ +import chalk from 'chalk' + +import { getWorkspace } from './lib/getWorkspace.js' + +async function main() { + const workspace = await getWorkspace() + await workspace.bumpVersion('canary') + await workspace.build() + await workspace.publishSync({ dryRun: false, tag: 'canary' }) + + header('๐ŸŽ‰ Done!') +} + +main().catch((error) => { + console.error(error) + process.exit(1) +}) + +function abort(message = 'Abort', exitCode = 1) { + console.error(chalk.bold.red(`\n${message}\n`)) + process.exit(exitCode) +} + +function header(message: string, opts?: { enable?: boolean }) { + const { enable } = opts ?? {} + if (!enable) { + return + } + + console.log(chalk.bold.green(`${message}\n`)) +} diff --git a/scripts/release.ts b/tools/releaser/src/release.ts similarity index 89% rename from scripts/release.ts rename to tools/releaser/src/release.ts index a1639b174..120bf951b 100755 --- a/scripts/release.ts +++ b/tools/releaser/src/release.ts @@ -7,13 +7,13 @@ import type { ExecSyncOptions } from 'child_process' +import { PROJECT_ROOT, ROOT_PACKAGE_JSON } from '@tools/constants' import chalk from 'chalk' import { loadChangelogConfig } from 'changelogen' import { execSync } from 'child_process' import execa from 'execa' import fse from 'fs-extra' import minimist from 'minimist' -import { fileURLToPath } from 'node:url' import path from 'path' import prompts from 'prompts' import semver from 'semver' @@ -26,12 +26,10 @@ import { createDraftGitHubRelease } from './utils/createDraftGitHubRelease.js' import { generateReleaseNotes } from './utils/generateReleaseNotes.js' import { getRecommendedBump } from './utils/getRecommendedBump.js' -const filename = fileURLToPath(import.meta.url) -const dirname = path.dirname(filename) -const cwd = path.resolve(dirname, '..') - -const execOpts: ExecSyncOptions = { stdio: 'inherit' } -const execaOpts: execa.Options = { stdio: 'inherit' } +// Always execute in project root +const cwd = PROJECT_ROOT +const execOpts: ExecSyncOptions = { stdio: 'inherit', cwd } +const execaOpts: execa.Options = { stdio: 'inherit', cwd } const args = minimist(process.argv.slice(2)) @@ -39,8 +37,8 @@ const { bump, // Semver release type: major, minor, patch, premajor, preminor, prepatch, prerelease changelog = false, // Whether to update the changelog. WARNING: This gets throttled on too many commits 'dry-run': dryRun, - 'git-tag': gitTag = true, // Whether to run git tag and commit operations 'git-commit': gitCommit = true, // Whether to run git commit operations + 'git-tag': gitTag = true, // Whether to run git tag and commit operations tag, // Tag to publish to: latest, beta, canary } = args @@ -67,6 +65,8 @@ const cmdRunnerAsync = } async function main() { + console.log({ projectRoot: PROJECT_ROOT }) + if (!process.env.GITHUB_TOKEN) { throw new Error('GITHUB_TOKEN env var is required') } @@ -108,7 +108,7 @@ async function main() { abort(`Prerelease bumps must have tag: beta or canary`) } - const monorepoVersion = fse.readJSONSync('package.json')?.version + const monorepoVersion = fse.readJSONSync(ROOT_PACKAGE_JSON)?.version if (!monorepoVersion) { throw new Error('Could not find version in package.json') @@ -125,14 +125,14 @@ async function main() { header(`${logPrefix}๐Ÿ“ Updating changelog...`) const { changelog: changelogContent, - releaseUrl: prefilledReleaseUrl, releaseNotes, + releaseUrl: prefilledReleaseUrl, } = await generateReleaseNotes({ bump, dryRun, - toVersion: 'HEAD', fromVersion, openReleaseUrl: true, + toVersion: 'HEAD', }) console.log(chalk.green('\nFull Release Notes:\n\n')) @@ -160,7 +160,8 @@ async function main() { await execa('pnpm', ['install'], execaOpts) - const buildResult = await execa('pnpm', ['build:all', '--output-logs=errors-only'], execaOpts) + // const buildResult = await execa('pnpm', ['build:all', '--output-logs=errors-only'], execaOpts) + const buildResult = await execa('pnpm', ['build:all'], execaOpts) if (buildResult.exitCode !== 0) { console.error(chalk.bold.red('Build failed')) console.log(buildResult.stderr) @@ -171,21 +172,21 @@ async function main() { header(`${logPrefix}๐Ÿ“ฆ Updating package.json versions...`) await Promise.all( packageDetails.map(async (pkg) => { - const packageJson = await fse.readJSON(`${pkg.packagePath}/package.json`) + const packageJsonPath = path.join(PROJECT_ROOT, `${pkg.packagePath}/package.json`) + const packageJson = await fse.readJSON(packageJsonPath) packageJson.version = nextReleaseVersion if (!dryRun) { - await fse.writeJSON(`${pkg.packagePath}/package.json`, packageJson, { spaces: 2 }) + await fse.writeJSON(packageJsonPath, packageJson, { spaces: 2 }) } }), ) // Set version in root package.json header(`${logPrefix}๐Ÿ“ฆ Updating root package.json...`) - const rootPackageJsonPath = path.resolve(dirname, '../package.json') - const rootPackageJson = await fse.readJSON(rootPackageJsonPath) + const rootPackageJson = await fse.readJSON(ROOT_PACKAGE_JSON) rootPackageJson.version = nextReleaseVersion if (!dryRun) { - await fse.writeJSON(rootPackageJsonPath, rootPackageJson, { spaces: 2 }) + await fse.writeJSON(ROOT_PACKAGE_JSON, rootPackageJson, { spaces: 2 }) } // Commit @@ -241,15 +242,19 @@ async function main() { try { const { releaseUrl: draftReleaseUrl } = await createDraftGitHubRelease({ branch: 'main', - tag: `v${nextReleaseVersion}`, releaseNotes, + tag: `v${nextReleaseVersion}`, }) console.log(chalk.bold.green(`Draft release created on GitHub: ${draftReleaseUrl}`)) - } catch (error) { + } catch (error: unknown) { console.log(chalk.bold.red('\nFull Release Notes:\n\n')) console.log(chalk.gray(releaseNotes) + '\n\n') console.log(`\n\nRelease URL: ${chalk.dim(prefilledReleaseUrl)}`) - console.log(chalk.bold.red(`Error creating draft release on GitHub: ${error.message}`)) + console.log( + chalk.bold.red( + `Error creating draft release on GitHub: ${error instanceof Error ? error.message : JSON.stringify(error)}`, + ), + ) console.log( chalk.bold.red( `Use the above link to create the release manually and optionally add the release notes.`, @@ -273,6 +278,7 @@ async function publishSinglePackage(pkg: PackageDetails, opts?: { dryRun?: boole const cmdArgs = ['publish', '-C', pkg.packagePath, '--no-git-checks', '--json', '--tag', tag] if (dryRun) { cmdArgs.push('--dry-run') + console.log(chalk.gray(`\n${logPrefix} pnpm ${cmdArgs.join(' ')}\n`)) } const { exitCode, stderr } = await execa('pnpm', cmdArgs, { cwd, @@ -300,8 +306,8 @@ async function publishSinglePackage(pkg: PackageDetails, opts?: { dryRun?: boole return { name: pkg.name, - success: false, details: `Exit Code: ${retryExitCode}, stderr: ${retryStdError}`, + success: false, } } @@ -311,11 +317,11 @@ async function publishSinglePackage(pkg: PackageDetails, opts?: { dryRun?: boole console.error(err) return { name: pkg.name, - success: false, details: err instanceof Error ? `Error publishing ${pkg.name}: ${err.message}` : `Unexpected error publishing ${pkg.name}: ${JSON.stringify(err)}`, + success: false, } } } @@ -370,7 +376,7 @@ function header(message: string, opts?: { enable?: boolean }) { } type PublishResult = { + details?: string name: string success: boolean - details?: string } diff --git a/scripts/utils/createDraftGitHubRelease.ts b/tools/releaser/src/utils/createDraftGitHubRelease.ts similarity index 100% rename from scripts/utils/createDraftGitHubRelease.ts rename to tools/releaser/src/utils/createDraftGitHubRelease.ts diff --git a/scripts/utils/generateReleaseNotes.ts b/tools/releaser/src/utils/generateReleaseNotes.ts similarity index 91% rename from scripts/utils/generateReleaseNotes.ts rename to tools/releaser/src/utils/generateReleaseNotes.ts index 14246d9a2..2a19c7dd7 100755 --- a/scripts/utils/generateReleaseNotes.ts +++ b/tools/releaser/src/utils/generateReleaseNotes.ts @@ -1,7 +1,6 @@ import type { GitCommit } from 'changelogen' import { execSync } from 'child_process' -import fse from 'fs-extra' import minimist from 'minimist' import open from 'open' import semver from 'semver' @@ -10,18 +9,14 @@ import { getLatestCommits } from './getLatestCommits.js' import { getRecommendedBump } from './getRecommendedBump.js' type Args = { - fromVersion?: string - toVersion?: string bump?: 'major' | 'minor' | 'patch' | 'prerelease' dryRun?: boolean + fromVersion?: string openReleaseUrl?: boolean + toVersion?: string } type ChangelogResult = { - /** - * URL to open releases/new with the changelog pre-filled - */ - releaseUrl: string /** * The changelog content, does not include contributors */ @@ -34,10 +29,14 @@ type ChangelogResult = { * The release tag, includes prefix 'v' */ releaseTag: string + /** + * URL to open releases/new with the changelog pre-filled + */ + releaseUrl: string } export const generateReleaseNotes = async (args: Args = {}): Promise => { - const { toVersion = 'HEAD', dryRun, bump, openReleaseUrl } = args + const { bump, dryRun, openReleaseUrl, toVersion = 'HEAD' } = args const fromVersion = args.fromVersion || execSync('git describe --match "v*" --tags --abbrev=0').toString().trim() @@ -62,11 +61,11 @@ export const generateReleaseNotes = async (args: Args = {}): Promise = { - feat: '๐Ÿš€ Features', - fix: '๐Ÿ› Bug Fixes', - perf: 'โšก Performance', - refactor: '๐Ÿ›  Refactors', - docs: '๐Ÿ“š Documentation', - style: '๐ŸŽจ Styles', - test: '๐Ÿงช Tests', - templates: '๐Ÿ“ Templates', - examples: '๐Ÿ““ Examples', - build: '๐Ÿ”จ Build', - ci: 'โš™๏ธ CI', - chore: '๐Ÿก Chores', + const emojiHeaderMap: Record = { breaking: 'โš ๏ธ BREAKING CHANGES', + build: '๏ฟฝ Build', + chore: '๐Ÿก Chores', + ci: 'โš™๏ธ CI', + docs: '๐Ÿ“š Documentation', + examples: '๐Ÿ““ Examples', + feat: '๐Ÿš€ Features', + fix: '๏ฟฝ Bug Fixes', + perf: 'โšก Performance', + refactor: '๏ฟฝ Refactors', + style: '๐ŸŽจ Styles', + templates: '๐Ÿ“ Templates', + test: '๐Ÿงช Tests', } const sections = conventionalCommits.reduce( @@ -111,16 +110,18 @@ export const generateReleaseNotes = async (args: Args = {}): Promise, + {} as Record, ) // Sort commits by scope, unscoped first @@ -160,10 +161,10 @@ export const generateReleaseNotes = async (args: Args = {}): Promise { continue } - const { author } = (await res.json()) as { author: { login: string; email: string } } + const { author } = (await res.json()) as { author: { email: string; login: string } } if (!contributors.some((c) => c.username === author.login)) { contributors.push({ name: commit.author.name, username: author.login }) @@ -225,7 +226,7 @@ async function getContributors(commits: GitCommit[]): Promise { const coAuthors = Array.from( commit.body.matchAll(coAuthorPattern), (match) => match.groups, - ).filter((e) => !e?.email.includes('[bot]')) as { name: string; email: string }[] + ).filter((e) => !e?.email?.includes('[bot]')) as { email: string; name: string }[] if (!coAuthors.length) { continue @@ -289,7 +290,7 @@ async function getContributors(commits: GitCommit[]): Promise { type Contributor = { name: string; username: string } function formatCommitForChangelog(commit: GitCommit, includeBreakingNotes = false): string { - const { scope, references, description, isBreaking } = commit + const { description, isBreaking, references, scope } = commit let formatted = `* ${scope ? `**${scope}:** ` : ''}${description}` references.forEach((ref) => { @@ -310,7 +311,7 @@ function formatCommitForChangelog(commit: GitCommit, includeBreakingNotes = fals let notes = ` ` + rawNotes - .split('\n') + ?.split('\n') .map((l) => ` ${l}`) // Indent notes .join('\n') .trim() @@ -329,13 +330,13 @@ function formatCommitForChangelog(commit: GitCommit, includeBreakingNotes = fals // module import workaround for ejs if (import.meta.url === `file://${process.argv[1]}`) { // This module is being run directly - const { fromVersion, toVersion, bump, openReleaseUrl } = minimist(process.argv.slice(2)) + const { bump, fromVersion, openReleaseUrl, toVersion } = minimist(process.argv.slice(2)) generateReleaseNotes({ bump, - fromVersion, - toVersion, dryRun: false, + fromVersion, openReleaseUrl, + toVersion, }) .then(() => { console.log('Done') diff --git a/scripts/utils/getLatestCommits.ts b/tools/releaser/src/utils/getLatestCommits.ts similarity index 100% rename from scripts/utils/getLatestCommits.ts rename to tools/releaser/src/utils/getLatestCommits.ts diff --git a/scripts/utils/getRecommendedBump.ts b/tools/releaser/src/utils/getRecommendedBump.ts similarity index 100% rename from scripts/utils/getRecommendedBump.ts rename to tools/releaser/src/utils/getRecommendedBump.ts diff --git a/tools/releaser/tsconfig.json b/tools/releaser/tsconfig.json new file mode 100644 index 000000000..596bf924a --- /dev/null +++ b/tools/releaser/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "strict": true, + "strictNullChecks": true + } +} diff --git a/tools/scripts/package.json b/tools/scripts/package.json new file mode 100644 index 000000000..56e8e3da5 --- /dev/null +++ b/tools/scripts/package.json @@ -0,0 +1,34 @@ +{ + "name": "@tools/scripts", + "version": "0.0.1", + "description": "", + "keywords": [], + "license": "ISC", + "author": "", + "type": "module", + "exports": { + ".": { + "import": "./src/index.ts" + } + }, + "main": "src/index.ts", + "scripts": { + "build": "tsc", + "build-template-with-local-pkgs": "pnpm runts src/build-template-with-local-pkgs.ts", + "gen-templates": "pnpm runts src/generate-template-variations.ts", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "pack-all-to-dest": "pnpm runts src/pack-all-to-dest.ts", + "runts": "cross-env NODE_OPTIONS=--no-deprecation node --no-deprecation --import @swc-node/register/esm-register", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "@swc-node/register": "1.10.9", + "@tools/constants": "workspace:*", + "@tools/releaser": "workspace:*", + "chalk": "^4.1.2", + "changelogen": "^0.5.5", + "create-payload-app": "workspace:*", + "open": "^10.1.0" + } +} diff --git a/scripts/build-template-with-local-pkgs.ts b/tools/scripts/src/build-template-with-local-pkgs.ts similarity index 90% rename from scripts/build-template-with-local-pkgs.ts rename to tools/scripts/src/build-template-with-local-pkgs.ts index ce79fbdca..7e98c7bf1 100644 --- a/scripts/build-template-with-local-pkgs.ts +++ b/tools/scripts/src/build-template-with-local-pkgs.ts @@ -1,21 +1,20 @@ +import { TEMPLATES_DIR } from '@tools/constants' import chalk from 'chalk' import { exec as execOrig, execSync } from 'child_process' import fs from 'fs/promises' -import { fileURLToPath } from 'node:url' import path from 'path' -const filename = fileURLToPath(import.meta.url) -const dirname = path.dirname(filename) - main().catch((error) => { console.error(error) process.exit(1) }) async function main() { - const templateDir = path.resolve(dirname, '../templates') const templateName = process.argv[2] - const templatePath = path.join(templateDir, templateName) + if (!templateName) { + throw new Error('Please provide a template name') + } + const templatePath = path.join(TEMPLATES_DIR, templateName) const databaseConnection = process.argv[3] || 'mongodb://127.0.0.1/your-database-name' console.log({ diff --git a/scripts/generate-template-variations.ts b/tools/scripts/src/generate-template-variations.ts similarity index 94% rename from scripts/generate-template-variations.ts rename to tools/scripts/src/generate-template-variations.ts index a2b8f3b33..e385edbae 100644 --- a/scripts/generate-template-variations.ts +++ b/tools/scripts/src/generate-template-variations.ts @@ -9,45 +9,42 @@ * There is no way currently to have lint-staged ignore the templates directory. */ +import type { DbType, StorageAdapterType } from 'create-payload-app/types' + +import { PROJECT_ROOT, TEMPLATES_DIR } from '@tools/constants' import chalk from 'chalk' import { execSync } from 'child_process' import { configurePayloadConfig } from 'create-payload-app/lib/configure-payload-config.js' import { copyRecursiveSync } from 'create-payload-app/utils/copy-recursive-sync.js' import minimist from 'minimist' import * as fs from 'node:fs/promises' -import { fileURLToPath } from 'node:url' import path from 'path' -import type { DbType, StorageAdapterType } from '../packages/create-payload-app/src/types.js' - -const filename = fileURLToPath(import.meta.url) -const dirname = path.dirname(filename) - type TemplateVariations = { - /** package.json name */ - name: string /** Base template to copy from */ base?: string + configureConfig?: boolean + db: DbType /** Directory in templates dir */ dirname: string - db: DbType - storage: StorageAdapterType - sharp: boolean - vercelDeployButtonLink?: string envNames?: { dbUri: string } - /** - * @default false - */ - skipReadme?: boolean + generateLockfile?: boolean + /** package.json name */ + name: string + sharp: boolean skipConfig?: boolean /** * @default false */ skipDockerCompose?: boolean - configureConfig?: boolean - generateLockfile?: boolean + /** + * @default false + */ + skipReadme?: boolean + storage: StorageAdapterType + vercelDeployButtonLink?: string } main().catch((error) => { @@ -58,17 +55,20 @@ main().catch((error) => { async function main() { const args = minimist(process.argv.slice(2)) const template = args['template'] // template directory name - const templatesDir = path.resolve(dirname, '../templates') const templateRepoUrlBase = `https://github.com/payloadcms/payload/tree/main/templates` let variations: TemplateVariations[] = [ { name: 'payload-vercel-postgres-template', - dirname: 'with-vercel-postgres', db: 'vercel-postgres', - storage: 'vercelBlobStorage', + dirname: 'with-vercel-postgres', + envNames: { + // This will replace the process.env.DATABASE_URI to process.env.POSTGRES_URL + dbUri: 'POSTGRES_URL', + }, sharp: false, + storage: 'vercelBlobStorage', vercelDeployButtonLink: `https://vercel.com/new/clone?repository-url=` + encodeURI( @@ -78,18 +78,20 @@ async function main() { '&build-command=pnpm run ci' + '&stores=[{"type":"postgres"},{"type":"blob"}]', // Postgres and Vercel Blob Storage ), - envNames: { - // This will replace the process.env.DATABASE_URI to process.env.POSTGRES_URL - dbUri: 'POSTGRES_URL', - }, }, { name: 'payload-vercel-website-template', base: 'website', // This is the base template to copy from - dirname: 'with-vercel-website', db: 'vercel-postgres', - storage: 'vercelBlobStorage', + dirname: 'with-vercel-website', + envNames: { + // This will replace the process.env.DATABASE_URI to process.env.POSTGRES_URL + dbUri: 'POSTGRES_URL', + }, sharp: true, + skipDockerCompose: true, + skipReadme: true, + storage: 'vercelBlobStorage', vercelDeployButtonLink: `https://vercel.com/new/clone?repository-url=` + encodeURI( @@ -99,26 +101,23 @@ async function main() { '&build-command=pnpm run ci' + '&stores=[{"type":"postgres"},{"type":"blob"}]', // Postgres and Vercel Blob Storage ), - envNames: { - // This will replace the process.env.DATABASE_URI to process.env.POSTGRES_URL - dbUri: 'POSTGRES_URL', - }, - skipReadme: true, - skipDockerCompose: true, }, { name: 'payload-postgres-template', - dirname: 'with-postgres', db: 'postgres', - storage: 'localDisk', + dirname: 'with-postgres', sharp: true, + storage: 'localDisk', }, { name: 'payload-vercel-mongodb-template', - dirname: 'with-vercel-mongodb', db: 'mongodb', - storage: 'vercelBlobStorage', + dirname: 'with-vercel-mongodb', + envNames: { + dbUri: 'MONGODB_URI', + }, sharp: false, + storage: 'vercelBlobStorage', vercelDeployButtonLink: `https://vercel.com/new/clone?repository-url=` + encodeURI( @@ -129,18 +128,15 @@ async function main() { '&stores=[{"type":"blob"}]' + // Vercel Blob Storage '&integration-ids=oac_jnzmjqM10gllKmSrG0SGrHOH', // MongoDB Atlas ), - envNames: { - dbUri: 'MONGODB_URI', - }, }, { name: 'blank', - dirname: 'blank', db: 'mongodb', + dirname: 'blank', generateLockfile: true, - storage: 'localDisk', sharp: true, skipConfig: true, // Do not copy the payload.config.ts file from the base template + storage: 'localDisk', // The blank template is used as a base for create-payload-app functionality, // so we do not configure the payload.config.ts file, which leaves the placeholder comments. configureConfig: false, @@ -159,21 +155,21 @@ async function main() { for (const { name, base, - dirname, - db, - generateLockfile, - storage, - vercelDeployButtonLink, - envNames, - sharp, configureConfig, - skipReadme = false, + db, + dirname, + envNames, + generateLockfile, + sharp, skipConfig = false, skipDockerCompose = false, + skipReadme = false, + storage, + vercelDeployButtonLink, } of variations) { header(`Generating ${name}...`) - const destDir = path.join(templatesDir, dirname) - copyRecursiveSync(path.join(templatesDir, base || '_template'), destDir, [ + const destDir = path.join(TEMPLATES_DIR, dirname) + copyRecursiveSync(path.join(TEMPLATES_DIR, base || '_template'), destDir, [ 'node_modules', '\\*\\.tgz', '.next', @@ -190,32 +186,32 @@ async function main() { log('Configuring payload.config.ts') const configureArgs = { dbType: db, + envNames, packageJsonName: name, projectDirOrConfigPath: { projectDir: destDir }, - storageAdapter: storage, sharp, - envNames, + storageAdapter: storage, } await configurePayloadConfig(configureArgs) log('Configuring .env.example') // Replace DATABASE_URI with the correct env name if set await writeEnvExample({ + dbType: db, destDir, envNames, - dbType: db, }) } if (!skipReadme) { await generateReadme({ - destDir, data: { name, - description: name, // TODO: Add descriptions attributes: { db, storage }, + description: name, // TODO: Add descriptions ...(vercelDeployButtonLink && { vercelDeployButtonLink }), }, + destDir, }) } @@ -240,7 +236,7 @@ async function main() { const migrationDestDir = path.join(destDir, 'src/migrations') // Delete and recreate migrations directory - await fs.rm(migrationDestDir, { recursive: true, force: true }) + await fs.rm(migrationDestDir, { force: true, recursive: true }) await fs.mkdir(migrationDestDir, { recursive: true }) log(`Generating initial migrations in ${migrationDestDir}`) @@ -249,9 +245,9 @@ async function main() { cwd: destDir, env: { ...process.env, - PAYLOAD_SECRET: 'asecretsolongnotevensantacouldguessit', BLOB_READ_WRITE_TOKEN: 'vercel_blob_rw_TEST_asdf', DATABASE_URI: process.env.POSTGRES_URL || 'postgres://localhost:5432/your-database-name', + PAYLOAD_SECRET: 'asecretsolongnotevensantacouldguessit', }, }) } @@ -262,24 +258,23 @@ async function main() { log(`Done configuring payload config for ${destDir}/src/payload.config.ts`) } - // TODO: Run prettier manually on the generated files, husky blows up log('Running prettier on generated files...') - execSyncSafe(`pnpm prettier --write templates "*.{js,jsx,ts,tsx}"`) + execSyncSafe(`pnpm prettier --write templates "*.{js,jsx,ts,tsx}"`, { cwd: PROJECT_ROOT }) log('Template generation complete!') } async function generateReadme({ + data: { name, attributes, description, vercelDeployButtonLink }, destDir, - data: { name, description, attributes, vercelDeployButtonLink }, }: { - destDir: string data: { - name: string - description: string attributes: Pick + description: string + name: string vercelDeployButtonLink?: string } + destDir: string }) { let header = `# ${name}\n` if (vercelDeployButtonLink) { @@ -302,13 +297,13 @@ ${description} } async function writeEnvExample({ + dbType, destDir, envNames, - dbType, }: { + dbType: DbType destDir: string envNames?: TemplateVariations['envNames'] - dbType: DbType }) { const envExamplePath = path.join(destDir, '.env.example') const envFileContents = await fs.readFile(envExamplePath, 'utf8') @@ -363,7 +358,9 @@ function execSyncSafe(command: string, options?: Parameters[1]) execSync(command, { stdio: 'inherit', ...options }) } catch (error) { if (error instanceof Error) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const stderr = (error as any).stderr?.toString() + // eslint-disable-next-line @typescript-eslint/no-explicit-any const stdout = (error as any).stdout?.toString() if (stderr && stderr.trim()) { diff --git a/tools/scripts/src/index.ts b/tools/scripts/src/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/scripts/pack-all-to-dest.ts b/tools/scripts/src/pack-all-to-dest.ts similarity index 75% rename from scripts/pack-all-to-dest.ts rename to tools/scripts/src/pack-all-to-dest.ts index 22bd0848c..7dc32719e 100644 --- a/scripts/pack-all-to-dest.ts +++ b/tools/scripts/src/pack-all-to-dest.ts @@ -1,23 +1,15 @@ +import type { PackageDetails } from '@tools/releaser' import type { ExecSyncOptions } from 'child_process' -import type execa from 'execa' +import { PROJECT_ROOT } from '@tools/constants' +import { getPackageDetails } from '@tools/releaser' import chalk from 'chalk' import { exec as execOrig, execSync } from 'child_process' -import fse from 'fs-extra' import minimist from 'minimist' -import { fileURLToPath } from 'node:url' import path from 'path' import util from 'util' -import type { PackageDetails } from './lib/getPackageDetails.js' - -import { getPackageDetails } from './lib/getPackageDetails.js' - -const execOpts: ExecSyncOptions = { stdio: 'inherit' } -const execaOpts: execa.Options = { stdio: 'inherit' } - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) +const execOpts: ExecSyncOptions = { stdio: 'inherit', cwd: PROJECT_ROOT } const exec = util.promisify(execOrig) @@ -39,10 +31,10 @@ async function main() { throw new Error('--dest is required') } - const resolvedDest = path.resolve(dest) + const resolvedDest = path.resolve(path.isAbsolute(dest) ? dest : path.join(PROJECT_ROOT, dest)) const packageWhitelist = all - ? null + ? undefined : [ 'payload', 'db-mongodb', @@ -73,18 +65,18 @@ async function main() { const filtered = packageDetails.filter((p): p is Exclude => p !== null) if (!noBuild) { - execSync('pnpm build:all --output-logs=errors-only', { stdio: 'inherit' }) + execSync('pnpm build:all --output-logs=errors-only', execOpts) } header(`\nOutputting ${filtered.length} packages... ${chalk.white.bold(listPackages(filtered))}`) - header(`\n๐Ÿ“ฆ Packing all packages to ${dest}...`) + header(`\n๐Ÿ“ฆ Packing all packages to ${resolvedDest}...`) await Promise.all( filtered.map(async (p) => { - await exec(`pnpm pack -C ${p.packagePath} --pack-destination ${resolvedDest}`) + await exec(`pnpm pack -C ${p.packagePath} --pack-destination ${resolvedDest}`, execOpts) }), ) diff --git a/tools/scripts/tsconfig.json b/tools/scripts/tsconfig.json new file mode 100644 index 000000000..f9f8fe292 --- /dev/null +++ b/tools/scripts/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "strict": true, + } +} diff --git a/tsconfig.base.json b/tsconfig.base.json index e66f005b5..ffd7ec771 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -4,7 +4,6 @@ "strict": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true, - "composite": true, "declaration": true, "declarationMap": true,