diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dac7b33..5a5def5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: node-version: ${{ matrix.node-version }} - uses: ./.github/actions/yarn-nm-install - - run: yarn build + - run: yarn cli build Lint: runs-on: ubuntu-latest diff --git a/.github/workflows/release-insiders.yml b/.github/workflows/release-insiders.yml index a45db21..88417df 100644 --- a/.github/workflows/release-insiders.yml +++ b/.github/workflows/release-insiders.yml @@ -106,7 +106,7 @@ jobs: - uses: ./.github/actions/yarn-nm-install - name: Build the Theatre.js packages - run: yarn build + run: yarn cli build - name: Update .yarnrc.yml with the auth config for the npmPublishRegistry run: cat .github/.yarnrc.publish.yml >> .yarnrc.yml - name: Publish the Theatre.js packages diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6377d87..d92ed87 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,7 +50,7 @@ The quickest way to start tweaking things is to run the `playground` package. ```sh $ cd ./packages/playground $ yarn serve -$ yarn build +$ yarn cli build # or, shortcut: $ cd root $ yarn playground @@ -73,7 +73,7 @@ examples. You can do that by running the `build` command at the root of the repo: ```sh -$ yarn build +$ yarn cli build ``` Then build any of the examples: @@ -148,9 +148,9 @@ same version number. In order to publish to npm, you can run the `release` script from the root of the repo: ```sh -$ yarn release x.y.z # npm publish version x.y.z -$ yarn release x.y.z-dev.w # npm publish version x.y.z-dev.w and tag it as "dev" -$ yarn release x.y.z-rc.w # npm publish version x.y.z-rc.w and tag it as "rc" +$ yarn cli release x.y.z # npm publish version x.y.z +$ yarn cli release x.y.z-dev.w # npm publish version x.y.z-dev.w and tag it as "dev" +$ yarn cli release x.y.z-rc.w # npm publish version x.y.z-rc.w and tag it as "rc" ``` @@ -203,7 +203,7 @@ $ yarn lint:all $ yarn lint:all --fix # Build all the packages -$ yarn build +$ yarn cli build ``` diff --git a/compat-tests/scripts/scripts.ts b/compat-tests/scripts/scripts.ts index 6aa35a1..570deb1 100644 --- a/compat-tests/scripts/scripts.ts +++ b/compat-tests/scripts/scripts.ts @@ -364,8 +364,8 @@ async function releaseToVerdaccio() { }) try { - await $`yarn clean` - await $`yarn build` + await $`yarn cli clean` + await $`yarn cli build` await Promise.all( packagesToPublish.map(async (workspaceName) => { diff --git a/devEnv/cli.ts b/devEnv/cli.ts new file mode 100644 index 0000000..234c35b --- /dev/null +++ b/devEnv/cli.ts @@ -0,0 +1,471 @@ +import sade from 'sade' +import {$, fs, path} from '@cspotcode/zx' +import * as core from '@actions/core' +import * as os from 'os' + +const root = path.join(__dirname, '..') + +const prog = sade('cli').describe('CLI for Theatre.js development') + +// better quote function from https://github.com/google/zx/pull/167 +$.quote = function quote(arg) { + if (/^[a-z0-9/_.-]+$/i.test(arg)) { + return arg + } + return ( + `$'` + + arg + .replace(/\\/g, '\\\\') + .replace(/'/g, "\\'") + .replace(/\f/g, '\\f') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\t/g, '\\t') + .replace(/\v/g, '\\v') + .replace(/\0/g, '\\0') + + `'` + ) +} + +prog + .command( + 'build clean', + 'Cleans the build artifacts and output directories of all the main packages', + ) + .action(async () => { + const packages = [ + 'theatre', + '@theatre/dataverse', + '@theatre/react', + '@theatre/browser-bundles', + '@theatre/r3f', + 'theatric', + ] + + await Promise.all([ + ...packages.map((workspace) => $`yarn workspace ${workspace} run clean`), + ]) + }) + +prog.command('build', 'Builds all the main packages').action(async () => { + const packagesToBuild = [ + 'theatre', + '@theatre/dataverse', + '@theatre/react', + '@theatre/browser-bundles', + '@theatre/r3f', + 'theatric', + ] + async function build() { + await Promise.all([ + $`yarn run build:ts`, + ...packagesToBuild.map( + (workspace) => $`yarn workspace ${workspace} run build`, + ), + ]) + } + + void build() +}) + +prog + .command('release ', 'Releases all the main packages to npm') + .option('--skip-lint', 'Skip typecheck and lint') + .action(async (version, opts) => { + /** + * This script publishes all packages to npm. + * + * It assigns the same version number to all packages (like lerna's fixed mode). + **/ + const packagesToBuild = [ + 'theatre', + '@theatre/dataverse', + '@theatre/react', + '@theatre/browser-bundles', + '@theatre/r3f', + 'theatric', + ] + + const packagesToPublish = [ + '@theatre/core', + '@theatre/studio', + '@theatre/dataverse', + '@theatre/react', + '@theatre/browser-bundles', + '@theatre/r3f', + 'theatric', + ] + + /** + * All these packages will have the same version from monorepo/package.json + */ + const packagesWhoseVersionsShouldBump = [ + '.', + 'theatre', + 'theatre/core', + 'theatre/studio', + 'packages/dataverse', + 'packages/react', + 'packages/browser-bundles', + 'packages/r3f', + 'packages/theatric', + ] + + // our packages will check for this env variable to make sure their + // prepublish script is only called from the `$ cd /path/to/monorepo; yarn run release` + // @ts-ignore ignore + process.env.THEATRE_IS_PUBLISHING = true + + async function release() { + $.verbose = false + const gitTags = (await $`git tag --list`).toString().split('\n') + + if (typeof version !== 'string') { + console.error( + `You need to specify a version, like: $ yarn cli release 1.2.0-rc.4`, + ) + process.exit(1) + } else if ( + !version.match(/^[0-9]+\.[0-9]+\.[0-9]+(\-(dev|rc)\.[0-9]+)?$/) + ) { + console.error( + `Use a semver version, like 1.2.3-rc.4. Provided: ${version}`, + ) + process.exit(1) + } + + const previousVersion = require('../package.json').version + + if (version === previousVersion) { + console.error( + `Version ${version} is already assigned to root/package.json`, + ) + process.exit(1) + } + + if (gitTags.some((tag) => tag === version)) { + console.error(`There is already a git tag for version ${version}`) + process.exit(1) + } + + let npmTag = 'latest' + if (version.match(/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/)) { + console.log('npm tag: latest') + } else { + const matches = version.match( + /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\-(dev|rc|beta)\.[0-9]{1,3}$/, + ) + if (!matches) { + console.log( + 'Invalid version. Currently xx.xx.xx or xx.xx.xx-(dev|rc|beta).xx is allowed', + ) + process.exit(1) + } + npmTag = matches[1] + console.log('npm tag: ' + npmTag) + } + + if ((await $`git status -s`).toString().length > 0) { + console.error(`Git working directory contains uncommitted changes:`) + $.verbose = true + await $`git status -s` + console.log('Commit/stash them and try again.') + process.exit(1) + } + + $.verbose = true + if (opts['skip-lint'] !== true) { + console.log('Running a typecheck and lint pass') + await Promise.all([$`yarn run typecheck`, $`yarn run lint:all`]) + } else { + console.log('Skipping typecheck and lint') + } + + const skipTypescriptEmit = argv['skip-ts'] === true + + console.log('Assigning versions') + await writeVersionsToPackageJSONs(version) + + console.log('Building all packages') + await Promise.all( + packagesToBuild.map((workspace) => + skipTypescriptEmit + ? $`yarn workspace ${workspace} run build:js` + : $`yarn workspace ${workspace} run build`, + ), + ) + + // temporarily rolling back the version assignments to make sure they don't show + // up in `$ git status`. (would've been better to just ignore hese particular changes + // but i'm lazy) + await restoreVersions() + + console.log( + 'Checking if the build produced artifacts that must first be comitted to git', + ) + $.verbose = false + if ((await $`git status -s`).toString().length > 0) { + $.verbose = true + await $`git status -s` + console.error(`Git directory contains uncommitted changes.`) + process.exit(1) + } + + $.verbose = true + + await writeVersionsToPackageJSONs(version) + + console.log('Committing/tagging') + + await $`git add .` + await $`git commit -m ${version}` + await $`git tag ${version}` + + // if (!gitTags.some((tag) => tag === version)) { + // console.log( + // `No git tag found for version "${version}". Run \`$ git tag ${version}\` and try again.`, + // ) + // process.exit() + // } + + console.log('Publishing to npm') + // await Promise.all( + // packagesToPublish.map( + // (workspace) => + // $`yarn workspace ${workspace} npm publish --access public --tag ${npmTag}`, + // ), + // ) + console.log('NOT!!') + } + + void release() + + async function writeVersionsToPackageJSONs(monorepoVersion: string) { + for (const packagePathRelativeFromRoot of packagesWhoseVersionsShouldBump) { + const pathToPackage = path.resolve( + __dirname, + '../', + packagePathRelativeFromRoot, + './package.json', + ) + + const original = JSON.parse( + fs.readFileSync(pathToPackage, {encoding: 'utf-8'}), + ) + + const newJson = {...original, version: monorepoVersion} + fs.writeFileSync( + path.join(pathToPackage), + JSON.stringify(newJson, undefined, 2), + {encoding: 'utf-8'}, + ) + await $`prettier --write ${ + packagePathRelativeFromRoot + '/package.json' + }` + } + } + + async function restoreVersions() { + const wasVerbose = $.verbose + $.verbose = false + for (const packagePathRelativeFromRoot of packagesWhoseVersionsShouldBump) { + const pathToPackageInGit = packagePathRelativeFromRoot + '/package.json' + + await $`git checkout ${pathToPackageInGit}` + } + $.verbose = wasVerbose + } + }) + +prog + .command( + 'prerelease ci', + "This script publishes the insider packages from the CI. You can't run it locally unless you have a a valid npm access token and you store its value in the `NPM_TOKEN` environmental variable.", + ) + .action(async () => { + const packagesToPublish = [ + '@theatre/core', + '@theatre/studio', + '@theatre/dataverse', + '@theatre/react', + '@theatre/browser-bundles', + '@theatre/r3f', + 'theatric', + ] + + /** + * Receives a version number and returns it without the tags, if there are any + * + * @param version - Version number + * @returns Version number without the tags + * + * @example + * ```javascript + * const version_1 = '0.4.8-dev3-ec175817' + * const version_2 = '0.4.8' + * + * stripTag(version_1) === stripTag(version_2) === '0.4.8' // returns `true` + * ``` + */ + function stripTag(version: string) { + const regExp = /^[0-9]+\.[0-9]+\.[0-9]+/g + const matches = version.match(regExp) + if (!matches) { + throw new Error(`Version number not found in "${version}"`) + } + + return matches[0] + } + + /** + * Creates a version number like `0.4.8-insiders.ec175817` + * + * @param packageName - Name of the package + * @param commitHash - A commit hash + */ + function getNewVersionName(packageName: string, commitHash: string) { + // The `r3f` package has its own release schedule, so its version numbers + // are almost always different from the rest of the packages. + const pathToPackageJson = + packageName === '@theatre/r3f' + ? path.resolve(__dirname, '../', 'packages', 'r3f', 'package.json') + : path.resolve(__dirname, '../', './package.json') + + const jsonData = JSON.parse( + fs.readFileSync(pathToPackageJson, {encoding: 'utf-8'}), + ) + const strippedVersion = stripTag(jsonData.version) + + return `${strippedVersion}-insiders.${commitHash}` + } + + /** + * Assigns the latest version names ({@link getNewVersionName}) to the packages' `package.json`s + * + * @param workspacesListObjects - An Array of objects containing information about the workspaces + * @param latestCommitHash - Hash of the latest commit + * @returns - A record of `{[packageId]: assignedVersion}` + */ + async function writeVersionsToPackageJSONs( + workspacesListObjects: {name: string; location: string}[], + latestCommitHash: string, + ): Promise> { + const assignedVersionByPackageName: Record = {} + for (const workspaceData of workspacesListObjects) { + const pathToPackage = path.resolve( + __dirname, + '../', + workspaceData.location, + './package.json', + ) + + const original = JSON.parse( + fs.readFileSync(pathToPackage, {encoding: 'utf-8'}), + ) + + let {version, dependencies, peerDependencies, devDependencies} = + original + // The @theatre/r3f package curently doesn't track the same version number of the other packages like @theatre/core, + // so we need to generate version numbers independently for each package + version = getNewVersionName(workspaceData.name, latestCommitHash) + assignedVersionByPackageName[workspaceData.name] = version + // Normally we don't have to override the package versions in dependencies because yarn would already convert + // all the "workspace:*" versions to a fixed version before publishing. However, packages like @theatre/studio + // have a peerDependency on @theatre/core set to "*" (meaning they would work with any version of @theatre/core). + // This is not the desired behavior in pre-release versions, so here, we'll fix those "*" versions to the set version. + for (const deps of [dependencies, peerDependencies, devDependencies]) { + if (!deps) continue + for (const wpObject of workspacesListObjects) { + if (deps[wpObject.name]) { + deps[wpObject.name] = getNewVersionName( + wpObject.name, + latestCommitHash, + ) + } + } + } + const newJson = { + ...original, + version, + dependencies, + peerDependencies, + devDependencies, + } + fs.writeFileSync( + path.join(pathToPackage), + JSON.stringify(newJson, undefined, 2), + {encoding: 'utf-8'}, + ) + await $`prettier --write ${workspaceData.location + '/package.json'}` + } + return assignedVersionByPackageName + } + + async function prerelease() { + // @ts-ignore ignore + process.env.THEATRE_IS_PUBLISHING = true + // In the CI `git log -1` points to a fake merge commit, + // so we have to use the value of a special GitHub context variable + // through the `GITHUB_SHA` environmental variable. + + // The length of the abbreviated commit hash can change, that's why we + // need the length of the fake merge commit's abbreviated hash. + const fakeMergeCommitHashLength = (await $`git log -1 --pretty=format:%h`) + .stdout.length + + if (!process.env.GITHUB_SHA) + throw new Error( + 'expected `process.env.GITHUB_SHA` to be defined but it was not', + ) + + const latestCommitHash = process.env.GITHUB_SHA.slice( + 0, + fakeMergeCommitHashLength, + ) + + const workspacesListString = await $`yarn workspaces list --json` + const workspacesListObjects = workspacesListString.stdout + .split(os.EOL) + // strip out empty lines + .filter(Boolean) + .map((x) => JSON.parse(x)) + + const assignedVersionByPackageName = await writeVersionsToPackageJSONs( + workspacesListObjects, + latestCommitHash, + ) + + await Promise.all( + packagesToPublish.map(async (workspaceName) => { + const npmTag = 'insiders' + if (process.env.GITHUB_ACTIONS) { + await $`yarn workspace ${workspaceName} npm publish --access public --tag ${npmTag}` + } + }), + ) + + if (process.env.GITHUB_ACTIONS) { + const data = packagesToPublish.map((packageName) => ({ + packageName, + version: assignedVersionByPackageName[packageName], + })) + + // set the output for github actions. + core.setOutput('data', JSON.stringify(data)) + } else { + for (const packageName of packagesToPublish) { + await $`echo ${`Published ${packageName}@${assignedVersionByPackageName[packageName]}`}` + } + } + } + + void prerelease() + }) + +prog + .command('dev all', 'Starts all services to develop all of the packages') + .action(async () => { + await $`yarn workspace playground run serve` + }) + +prog.parse(process.argv) diff --git a/devEnv/scripts/build.ts b/devEnv/scripts/build.ts deleted file mode 100644 index 37b6afe..0000000 --- a/devEnv/scripts/build.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {$} from '@cspotcode/zx' -/** - * Builds all the packages for production - */ - -const packagesToBuild = [ - 'theatre', - '@theatre/dataverse', - '@theatre/react', - '@theatre/browser-bundles', - '@theatre/r3f', - 'theatric', -] -async function build() { - // better quote function from https://github.com/google/zx/pull/167 - $.quote = function quote(arg) { - if (/^[a-z0-9/_.-]+$/i.test(arg)) { - return arg - } - return ( - `$'` + - arg - .replace(/\\/g, '\\\\') - .replace(/'/g, "\\'") - .replace(/\f/g, '\\f') - .replace(/\n/g, '\\n') - .replace(/\r/g, '\\r') - .replace(/\t/g, '\\t') - .replace(/\v/g, '\\v') - .replace(/\0/g, '\\0') + - `'` - ) - } - - await Promise.all([ - $`yarn run build:ts`, - ...packagesToBuild.map( - (workspace) => $`yarn workspace ${workspace} run build`, - ), - ]) -} - -void build() diff --git a/devEnv/scripts/clean.ts b/devEnv/scripts/clean.ts deleted file mode 100644 index e8ce52a..0000000 --- a/devEnv/scripts/clean.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {$} from '@cspotcode/zx' - -/** - * cleans the build artifacts of all packages - */ - -const packages = [ - 'theatre', - '@theatre/dataverse', - '@theatre/react', - '@theatre/browser-bundles', - '@theatre/r3f', - 'theatric', -] - -async function clean() { - // better quote function from https://github.com/google/zx/pull/167 - $.quote = function quote(arg) { - if (/^[a-z0-9/_.-]+$/i.test(arg)) { - return arg - } - return ( - `$'` + - arg - .replace(/\\/g, '\\\\') - .replace(/'/g, "\\'") - .replace(/\f/g, '\\f') - .replace(/\n/g, '\\n') - .replace(/\r/g, '\\r') - .replace(/\t/g, '\\t') - .replace(/\v/g, '\\v') - .replace(/\0/g, '\\0') + - `'` - ) - } - - await Promise.all([ - ...packages.map((workspace) => $`yarn workspace ${workspace} run clean`), - ]) -} - -void clean() diff --git a/devEnv/scripts/prerelease.ts b/devEnv/scripts/prerelease.ts deleted file mode 100644 index 0b8f8b8..0000000 --- a/devEnv/scripts/prerelease.ts +++ /dev/null @@ -1,185 +0,0 @@ -/** - * This script publishes the insider packages from the CI. You can't run it locally unless you have a a valid npm access token and you store its value in the `NPM_TOKEN` environmental variable. - */ - -import * as os from 'os' -import * as path from 'path' -import * as core from '@actions/core' -import {$} from '@cspotcode/zx' - -const packagesToPublish = [ - '@theatre/core', - '@theatre/studio', - '@theatre/dataverse', - '@theatre/react', - '@theatre/browser-bundles', - '@theatre/r3f', - 'theatric', -] - -/** - * Receives a version number and returns it without the tags, if there are any - * - * @param version - Version number - * @returns Version number without the tags - * - * @example - * ```javascript - * const version_1 = '0.4.8-dev3-ec175817' - * const version_2 = '0.4.8' - * - * stripTag(version_1) === stripTag(version_2) === '0.4.8' // returns `true` - * ``` - */ -function stripTag(version: string) { - const regExp = /^[0-9]+\.[0-9]+\.[0-9]+/g - const matches = version.match(regExp) - if (!matches) { - throw new Error(`Version number not found in "${version}"`) - } - - return matches[0] -} - -/** - * Creates a version number like `0.4.8-insiders.ec175817` - * - * @param packageName - Name of the package - * @param commitHash - A commit hash - */ -function getNewVersionName(packageName: string, commitHash: string) { - // The `r3f` package has its own release schedule, so its version numbers - // are almost always different from the rest of the packages. - const pathToPackageJson = - packageName === '@theatre/r3f' - ? path.resolve(__dirname, '../..', 'packages', 'r3f', 'package.json') - : path.resolve(__dirname, '../../', './package.json') - - const jsonData = JSON.parse( - fs.readFileSync(pathToPackageJson, {encoding: 'utf-8'}), - ) - const strippedVersion = stripTag(jsonData.version) - - return `${strippedVersion}-insiders.${commitHash}` -} - -/** - * Assigns the latest version names ({@link getNewVersionName}) to the packages' `package.json`s - * - * @param workspacesListObjects - An Array of objects containing information about the workspaces - * @param latestCommitHash - Hash of the latest commit - * @returns - A record of `{[packageId]: assignedVersion}` - */ -async function writeVersionsToPackageJSONs( - workspacesListObjects: {name: string; location: string}[], - latestCommitHash: string, -): Promise> { - const assignedVersionByPackageName: Record = {} - for (const workspaceData of workspacesListObjects) { - const pathToPackage = path.resolve( - __dirname, - '../..', - workspaceData.location, - './package.json', - ) - - const original = JSON.parse( - fs.readFileSync(pathToPackage, {encoding: 'utf-8'}), - ) - - let {version, dependencies, peerDependencies, devDependencies} = original - // The @theatre/r3f package curently doesn't track the same version number of the other packages like @theatre/core, - // so we need to generate version numbers independently for each package - version = getNewVersionName(workspaceData.name, latestCommitHash) - assignedVersionByPackageName[workspaceData.name] = version - // Normally we don't have to override the package versions in dependencies because yarn would already convert - // all the "workspace:*" versions to a fixed version before publishing. However, packages like @theatre/studio - // have a peerDependency on @theatre/core set to "*" (meaning they would work with any version of @theatre/core). - // This is not the desired behavior in pre-release versions, so here, we'll fix those "*" versions to the set version. - for (const deps of [dependencies, peerDependencies, devDependencies]) { - if (!deps) continue - for (const wpObject of workspacesListObjects) { - if (deps[wpObject.name]) { - deps[wpObject.name] = getNewVersionName( - wpObject.name, - latestCommitHash, - ) - } - } - } - const newJson = { - ...original, - version, - dependencies, - peerDependencies, - devDependencies, - } - fs.writeFileSync( - path.join(pathToPackage), - JSON.stringify(newJson, undefined, 2), - {encoding: 'utf-8'}, - ) - await $`prettier --write ${workspaceData.location + '/package.json'}` - } - return assignedVersionByPackageName -} - -async function prerelease() { - // @ts-ignore ignore - process.env.THEATRE_IS_PUBLISHING = true - // In the CI `git log -1` points to a fake merge commit, - // so we have to use the value of a special GitHub context variable - // through the `GITHUB_SHA` environmental variable. - - // The length of the abbreviated commit hash can change, that's why we - // need the length of the fake merge commit's abbreviated hash. - const fakeMergeCommitHashLength = (await $`git log -1 --pretty=format:%h`) - .stdout.length - - if (!process.env.GITHUB_SHA) - throw new Error( - 'expected `process.env.GITHUB_SHA` to be defined but it was not', - ) - - const latestCommitHash = process.env.GITHUB_SHA.slice( - 0, - fakeMergeCommitHashLength, - ) - - const workspacesListString = await $`yarn workspaces list --json` - const workspacesListObjects = workspacesListString.stdout - .split(os.EOL) - // strip out empty lines - .filter(Boolean) - .map((x) => JSON.parse(x)) - - const assignedVersionByPackageName = await writeVersionsToPackageJSONs( - workspacesListObjects, - latestCommitHash, - ) - - await Promise.all( - packagesToPublish.map(async (workspaceName) => { - const npmTag = 'insiders' - if (process.env.GITHUB_ACTIONS) { - await $`yarn workspace ${workspaceName} npm publish --access public --tag ${npmTag}` - } - }), - ) - - if (process.env.GITHUB_ACTIONS) { - const data = packagesToPublish.map((packageName) => ({ - packageName, - version: assignedVersionByPackageName[packageName], - })) - - // set the output for github actions. - core.setOutput('data', JSON.stringify(data)) - } else { - for (const packageName of packagesToPublish) { - await $`echo ${`Published ${packageName}@${assignedVersionByPackageName[packageName]}`}` - } - } -} - -void prerelease() diff --git a/devEnv/scripts/release.ts b/devEnv/scripts/release.ts deleted file mode 100644 index 2ff9bbd..0000000 --- a/devEnv/scripts/release.ts +++ /dev/null @@ -1,227 +0,0 @@ -/** - * The release script. This must be called with zx from the repo's root. - * Example: - * ``` - * $ cd /path/to/repo - * $ yarn run release 0.4.2 - * ``` - */ -import * as path from 'path' -import {readFileSync, writeFileSync} from 'fs' -import {$, argv} from '@cspotcode/zx' - -/** - * This script publishes all packages to npm. - * - * It assigns the same version number to all packages (like lerna's fixed mode). - **/ -const packagesToBuild = [ - 'theatre', - '@theatre/dataverse', - '@theatre/react', - '@theatre/browser-bundles', - '@theatre/r3f', - 'theatric', -] - -const packagesToPublish = [ - '@theatre/core', - '@theatre/studio', - '@theatre/dataverse', - '@theatre/react', - '@theatre/browser-bundles', - '@theatre/r3f', - 'theatric', -] - -/** - * All these packages will have the same version from monorepo/package.json - */ -const packagesWhoseVersionsShouldBump = [ - '.', - 'theatre', - 'theatre/core', - 'theatre/studio', - 'packages/dataverse', - 'packages/react', - 'packages/browser-bundles', - 'packages/r3f', - 'packages/theatric', -] - -// our packages will check for this env variable to make sure their -// prepublish script is only called from the `$ cd /path/to/monorepo; yarn run release` -// @ts-ignore ignore -process.env.THEATRE_IS_PUBLISHING = true - -// better quote function from https://github.com/google/zx/pull/167 -$.quote = function quote(arg) { - if (/^[a-z0-9/_.-]+$/i.test(arg)) { - return arg - } - return ( - `$'` + - arg - .replace(/\\/g, '\\\\') - .replace(/'/g, "\\'") - .replace(/\f/g, '\\f') - .replace(/\n/g, '\\n') - .replace(/\r/g, '\\r') - .replace(/\t/g, '\\t') - .replace(/\v/g, '\\v') - .replace(/\0/g, '\\0') + - `'` - ) -} - -async function release() { - $.verbose = false - const gitTags = (await $`git tag --list`).toString().split('\n') - - const version = argv._[argv._.length - 1] - if (typeof version !== 'string') { - console.error( - `You need to specify a version, like: $ yarn release 1.2.0-rc.4`, - ) - process.exit(1) - } else if (!version.match(/^[0-9]+\.[0-9]+\.[0-9]+(\-(dev|rc)\.[0-9]+)?$/)) { - console.error(`Use a semver version, like 1.2.3-rc.4. Provided: ${version}`) - process.exit(1) - } - - const previousVersion = require('../../package.json').version - - if (version === previousVersion) { - console.error(`Version ${version} is already assigned to root/package.json`) - process.exit(1) - } - - if (gitTags.some((tag) => tag === version)) { - console.error(`There is already a git tag for version ${version}`) - process.exit(1) - } - - let npmTag = 'latest' - if (version.match(/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/)) { - console.log('npm tag: latest') - } else { - const matches = version.match( - /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\-(dev|rc|beta)\.[0-9]{1,3}$/, - ) - if (!matches) { - console.log( - 'Invalid version. Currently xx.xx.xx or xx.xx.xx-(dev|rc|beta).xx is allowed', - ) - process.exit(1) - } - npmTag = matches[1] - console.log('npm tag: ' + npmTag) - } - - if ((await $`git status -s`).toString().length > 0) { - console.error(`Git working directory contains uncommitted changes:`) - $.verbose = true - await $`git status -s` - console.log('Commit/stash them and try again.') - process.exit(1) - } - - $.verbose = true - if (argv['skip-lint'] !== true) { - console.log('Running a typecheck and lint pass') - await Promise.all([$`yarn run typecheck`, $`yarn run lint:all`]) - } else { - console.log('Skipping typecheck and lint') - } - - const skipTypescriptEmit = argv['skip-ts'] === true - - console.log('Assigning versions') - await writeVersionsToPackageJSONs(version) - - console.log('Building all packages') - await Promise.all( - packagesToBuild.map((workspace) => - skipTypescriptEmit - ? $`yarn workspace ${workspace} run build:js` - : $`yarn workspace ${workspace} run build`, - ), - ) - - // temporarily rolling back the version assignments to make sure they don't show - // up in `$ git status`. (would've been better to just ignore hese particular changes - // but i'm lazy) - await restoreVersions() - - console.log( - 'Checking if the build produced artifacts that must first be comitted to git', - ) - $.verbose = false - if ((await $`git status -s`).toString().length > 0) { - $.verbose = true - await $`git status -s` - console.error(`Git directory contains uncommitted changes.`) - process.exit(1) - } - - $.verbose = true - - await writeVersionsToPackageJSONs(version) - - console.log('Committing/tagging') - - await $`git add .` - await $`git commit -m ${version}` - await $`git tag ${version}` - - // if (!gitTags.some((tag) => tag === version)) { - // console.log( - // `No git tag found for version "${version}". Run \`$ git tag ${version}\` and try again.`, - // ) - // process.exit() - // } - - console.log('Publishing to npm') - await Promise.all( - packagesToPublish.map( - (workspace) => - $`yarn workspace ${workspace} npm publish --access public --tag ${npmTag}`, - ), - ) -} - -void release() - -async function writeVersionsToPackageJSONs(monorepoVersion: string) { - for (const packagePathRelativeFromRoot of packagesWhoseVersionsShouldBump) { - const pathToPackage = path.resolve( - __dirname, - '../..', - packagePathRelativeFromRoot, - './package.json', - ) - - const original = JSON.parse( - readFileSync(pathToPackage, {encoding: 'utf-8'}), - ) - - const newJson = {...original, version: monorepoVersion} - writeFileSync( - path.join(pathToPackage), - JSON.stringify(newJson, undefined, 2), - {encoding: 'utf-8'}, - ) - await $`prettier --write ${packagePathRelativeFromRoot + '/package.json'}` - } -} - -async function restoreVersions() { - const wasVerbose = $.verbose - $.verbose = false - for (const packagePathRelativeFromRoot of packagesWhoseVersionsShouldBump) { - const pathToPackageInGit = packagePathRelativeFromRoot + '/package.json' - - await $`git checkout ${pathToPackageInGit}` - } - $.verbose = wasVerbose -} diff --git a/package.json b/package.json index ead2155..090b981 100644 --- a/package.json +++ b/package.json @@ -9,19 +9,17 @@ "compat-tests" ], "scripts": { + "cli": "node -r esbuild-register devEnv/cli.ts", "playground": "yarn workspace playground run serve", "benchmarks": "yarn workspace benchmarks run serve", "test:e2e": "yarn workspace playground run test", "test:e2e:ci": "yarn workspace playground run test:ci", "typecheck": "yarn run build:ts", - "build": "node -r esbuild-register devEnv/scripts/build.ts", - "clean": "node -r esbuild-register devEnv/scripts/clean.ts", "build:ts": "tsc --build ./devEnv/typecheck-all-projects/tsconfig.all.json", "test": "jest", "test:compat:install": "yarn workspace @theatre/compat-tests run install-fixtures", "test:compat:run": "jest --config jest.compat-tests.config.js", "postinstall": "husky install", - "release": "node -r esbuild-register devEnv/scripts/release.ts", "lint:all": "eslint . --ext ts,tsx --ignore-path=.gitignore --rulesdir ./devEnv/eslint/rules" }, "lint-staged": { @@ -62,6 +60,7 @@ }, "packageManager": "yarn@3.2.0", "dependencies": { - "@actions/core": "^1.10.0" + "@actions/core": "^1.10.0", + "sade": "^1.8.1" } } diff --git a/yarn.lock b/yarn.lock index 3a6ebe5..6b772de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23463,6 +23463,13 @@ fsevents@^1.2.7: languageName: node linkType: hard +"mri@npm:^1.1.0": + version: 1.2.0 + resolution: "mri@npm:1.2.0" + checksum: 83f515abbcff60150873e424894a2f65d68037e5a7fcde8a9e2b285ee9c13ac581b63cfc1e6826c4732de3aeb84902f7c1e16b7aff46cd3f897a0f757a894e85 + languageName: node + linkType: hard + "ms@npm:2.0.0": version: 2.0.0 resolution: "ms@npm:2.0.0" @@ -29135,6 +29142,15 @@ fsevents@^1.2.7: languageName: node linkType: hard +"sade@npm:^1.8.1": + version: 1.8.1 + resolution: "sade@npm:1.8.1" + dependencies: + mri: ^1.1.0 + checksum: 0756e5b04c51ccdc8221ebffd1548d0ce5a783a44a0fa9017a026659b97d632913e78f7dca59f2496aa996a0be0b0c322afd87ca72ccd909406f49dbffa0f45d + languageName: node + linkType: hard + "safari-14-idb-fix@npm:^3.0.0": version: 3.0.0 resolution: "safari-14-idb-fix@npm:3.0.0" @@ -31361,6 +31377,7 @@ fsevents@^1.2.7: lint-staged: ^13.0.3 node-gyp: ^9.1.0 prettier: ^2.3.2 + sade: ^1.8.1 typescript: 5.1.6 yaml: ^2.3.1 languageName: unknown