Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(ci): update yarn rwfw project:tarsync to handle react resolutions #10229

Merged
merged 1 commit into from
Mar 15, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 126 additions & 88 deletions tasks/framework-tools/tarsync.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/usr/bin/env node
/* eslint-env node */

import { performance } from 'node:perf_hooks'
import { fileURLToPath } from 'node:url'
Expand All @@ -8,12 +7,90 @@ import { parseArgs as nodeUtilParseArgs } from 'node:util'
import ora from 'ora'
import { cd, chalk, fs, glob, path, within, $ } from 'zx'

const mockSpinner = {
text: '',
succeed: () => {},
}
const FRAMEWORK_PATH = fileURLToPath(new URL('../../', import.meta.url))
const TARBALL_DEST_DIRNAME = 'tarballs'

async function main() {
const { projectPath, verbose } = await getOptions()
$.verbose = verbose

cd(FRAMEWORK_PATH)
performance.mark('startFramework')

const spinner = getFrameworkSpinner({ text: 'building and packing packages' })
await buildTarballs()

spinner.text = 'moving tarballs'
await moveTarballs(projectPath)

spinner.text = 'updating resolutions'
await updateResolutions(projectPath)

performance.mark('endFramework')
performance.measure('framework', 'startFramework', 'endFramework')
const [entry] = performance.getEntriesByName('framework')
spinner.succeed(`finished in ${(entry.duration / 1000).toFixed(2)} seconds`)

await yarnInstall(projectPath)

const entries = performance.getEntriesByType('measure').map((entry) => {
return `• ${entry.name} => ${(entry.duration / 1000).toFixed(2)} seconds`
})

for (const entry of entries) {
verbose && console.log(entry)
}
}

main()

// Helpers
// -------

async function parseArgs() {
const { positionals, values } = nodeUtilParseArgs({
allowPositionals: true,

options: {
verbose: {
type: 'boolean',
default: false,
short: 'v',
},
},
})

const [projectPath] = positionals

const options = {
verbose: values.verbose,
}

options.projectPath = projectPath ? projectPath : process.env.RWJS_CWD

if (!options.projectPath) {
throw new Error(
[
'Error: You have to provide the path to a Redwood project as',
'',
' 1. the first positional argument',
'',
chalk.gray(' yarn project:tarsync /path/to/redwood/project'),
'',
' 2. the `RWJS_CWD` env var',
'',
chalk.gray(' RWJS_CWD=/path/to/redwood/project yarn project:tarsync'),
].join('\n')
)
}

// This makes `projectPath` an absolute path and throws if it doesn't exist.
options.projectPath = await fs.realpath(options.projectPath)

return options
}

async function getOptions() {
let options

try {
Expand All @@ -26,33 +103,35 @@ async function main() {

const { projectPath, verbose } = options

$.verbose = verbose

// Closing over `verbose` here.
function getProjectSpinner({ text }) {
return verbose
? mockSpinner
: ora({ prefixText: `${chalk.green('[ project ]')}`, text }).start()
return {
projectPath,
verbose,
}
}

function getFrameworkSpinner({ text }) {
return verbose
? mockSpinner
: ora({ prefixText: `${chalk.cyan('[framework]')}`, text }).start()
}
const mockSpinner = {
text: '',
succeed: () => { },
}

const frameworkPath = fileURLToPath(new URL('../../', import.meta.url))
cd(frameworkPath)
performance.mark('startFramework')
function getProjectSpinner({ text }) {
return $.verbose
? mockSpinner
: ora({ prefixText: `${chalk.green('[ project ]')}`, text }).start()
}

const spinner = getFrameworkSpinner({ text: 'building and packing packages' })
function getFrameworkSpinner({ text }) {
return $.verbose
? mockSpinner
: ora({ prefixText: `${chalk.cyan('[framework]')}`, text }).start()
}

async function buildTarballs() {
await $`yarn nx run-many -t build:pack --exclude create-redwood-app`
}

spinner.text = 'moving tarballs'

const tarballDestDirname = 'tarballs'
const tarballDest = path.join(projectPath, tarballDestDirname)
async function moveTarballs(projectPath) {
const tarballDest = path.join(projectPath, TARBALL_DEST_DIRNAME)
await fs.ensureDir(tarballDest)

const tarballs = await glob(['./packages/**/*.tgz'])
Expand All @@ -64,9 +143,25 @@ async function main() {
})
)
)
}

spinner.text = 'updating resolutions'
async function getReactResolutions() {
const packageConfig = await fs.readJson(path.join(FRAMEWORK_PATH, 'packages/web/package.json'))

const react = packageConfig.peerDependencies.react
const reactDom = packageConfig.peerDependencies['react-dom']

if (!react || !reactDom) {
throw new Error("Couldn't find react or react-dom in @redwoodjs/web's peerDependencies")
}

return {
react,
'react-dom': reactDom,
}
}

async function updateResolutions(projectPath) {
const resolutions = (await $`yarn workspaces list --json`).stdout
.trim()
.split('\n')
Expand All @@ -77,9 +172,8 @@ async function main() {
return {
...resolutions,
// Turn a Redwood package name like `@redwoodjs/project-config` into `redwoodjs-project-config.tgz`.
[name]: `./${tarballDestDirname}/${
name.replace('@', '').replaceAll('/', '-') + '.tgz'
}`,
[name]: `./${TARBALL_DEST_DIRNAME}/${name.replace('@', '').replaceAll('/', '-') + '.tgz'
}`,
}
}, {})

Expand All @@ -93,20 +187,16 @@ async function main() {
resolutions: {
...projectPackageJson.resolutions,
...resolutions,
...(await getReactResolutions())
},
},
{
spaces: 2,
}
)
}

performance.mark('endFramework')
performance.measure('framework', 'startFramework', 'endFramework')

const [entry] = performance.getEntriesByName('framework')

spinner.succeed(`finished in ${(entry.duration / 1000).toFixed(2)} seconds`)

async function yarnInstall(projectPath) {
await within(async () => {
cd(projectPath)
performance.mark('startProject')
Expand All @@ -123,56 +213,4 @@ async function main() {
spinner.succeed(`finished in ${(entry.duration / 1000).toFixed(2)} seconds`)
})

const entries = performance.getEntriesByType('measure').map((entry) => {
return `• ${entry.name} => ${(entry.duration / 1000).toFixed(2)} seconds`
})

for (const entry of entries) {
verbose && console.log(entry)
}
}

main()

async function parseArgs() {
const { positionals, values } = nodeUtilParseArgs({
allowPositionals: true,

options: {
verbose: {
type: 'boolean',
default: false,
short: 'v',
},
},
})

const [projectPath] = positionals

const options = {
verbose: values.verbose,
}

options.projectPath = projectPath ? projectPath : process.env.RWJS_CWD

if (!options.projectPath) {
throw new Error(
[
'Error: You have to provide the path to a Redwood project as',
'',
' 1. the first positional argument',
'',
chalk.gray(' yarn project:tarsync /path/to/redwood/project'),
'',
' 2. the `RWJS_CWD` env var',
'',
chalk.gray(' RWJS_CWD=/path/to/redwood/project yarn project:tarsync'),
].join('\n')
)
}

// This makes `projectPath` an absolute path and throws if it doesn't exist.
options.projectPath = await fs.realpath(options.projectPath)

return options
}
Loading