Fix github checks (#263)

* Update dependencies which pass tests locally

* Break the yarn cache in an attempt to fix checks

* playground: Add wrapping error info for build.ts

* playground: Don't use dev mode in CI tests

* playground: Use tsc for typecheck

* playground: Specify working esbuild version 0.13.15

* playground: Use only promises in build.ts

* playground: Ensure serving in ci e2e

 * Add echo for vercel for build:static
This commit is contained in:
Cole Lawrence 2022-07-25 04:37:34 -04:00 committed by GitHub
parent 00265471ba
commit 11b5d175f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 2981 additions and 1265 deletions

View file

@ -35,9 +35,11 @@ jobs:
path: | path: |
${{ steps.yarn-cache-dir-path.outputs.dir }} ${{ steps.yarn-cache-dir-path.outputs.dir }}
${{ github.workspace }}/compatibility-tests/*/.yarn/cache ${{ github.workspace }}/compatibility-tests/*/.yarn/cache
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} key:
${{ runner.os }}-${{ matrix.node-version }}-yarn-${{
hashFiles('**/yarn.lock') }}
restore-keys: | restore-keys: |
${{ runner.os }}-yarn- ${{ runner.os }}-${{ matrix.node-version }}-yarn-
- run: yarn install - run: yarn install
- run: yarn test:compatibility:ci - run: yarn test:compatibility:ci

View file

@ -35,9 +35,11 @@ jobs:
path: | path: |
${{ steps.yarn-cache-dir-path.outputs.dir }} ${{ steps.yarn-cache-dir-path.outputs.dir }}
${{ github.workspace }}/compatibility-tests/*/.yarn/cache ${{ github.workspace }}/compatibility-tests/*/.yarn/cache
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} key:
${{ runner.os }}-${{ matrix.node-version }}-yarn-${{
hashFiles('**/yarn.lock') }}
restore-keys: | restore-keys: |
${{ runner.os }}-yarn- ${{ runner.os }}-${{ matrix.node-version }}-yarn-
- run: yarn install - run: yarn install
- run: yarn lint:all --max-warnings 0 - run: yarn lint:all --max-warnings 0

View file

@ -1,3 +1,4 @@
/** @type {import('jest').Config} */
module.exports = { module.exports = {
testMatch: [ testMatch: [
'<rootDir>/packages/*/src/**/*.test.ts', '<rootDir>/packages/*/src/**/*.test.ts',

View file

@ -32,31 +32,31 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.15.0", "@babel/core": "^7.18.9",
"@babel/plugin-proposal-class-properties": "^7.14.5", "@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
"@babel/plugin-proposal-optional-chaining": "^7.14.5", "@babel/plugin-proposal-optional-chaining": "^7.18.9",
"@babel/preset-env": "^7.15.0", "@babel/preset-env": "^7.18.9",
"@babel/preset-react": "^7.14.5", "@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.15.0", "@babel/preset-typescript": "^7.18.6",
"@microsoft/api-documenter": "^7.13.54", "@microsoft/api-documenter": "^7.19.0",
"@microsoft/api-extractor": "^7.18.11", "@microsoft/api-extractor": "^7.28.6",
"@typescript-eslint/eslint-plugin": "^4.30.0", "@typescript-eslint/eslint-plugin": "^5.30.7",
"@typescript-eslint/parser": "^4.30.0", "@typescript-eslint/parser": "^5.30.7",
"esbuild": "^0.12.24", "esbuild": "^0.14.49",
"esbuild-jest": "^0.5.0", "esbuild-jest": "^0.5.0",
"eslint": "^7.32.0", "eslint": "^8.20.0",
"eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-react": "^7.25.1", "eslint-plugin-react": "^7.30.1",
"eslint-plugin-tsdoc": "^0.2.14", "eslint-plugin-tsdoc": "^0.2.16",
"eslint-plugin-unused-imports": "^1.1.4", "eslint-plugin-unused-imports": "^2.0.0",
"husky": "^6.0.0", "husky": "^6.0.0",
"jest": "^27.1.0", "jest": "^27.1.0",
"jsonc-parser": "^3.0.0", "jsonc-parser": "^3.1.0",
"lint-staged": "^11.1.2", "lint-staged": "^13.0.3",
"node-gyp": "^8.1.0", "node-gyp": "^9.1.0",
"prettier": "^2.3.2", "prettier": "^2.3.2",
"typescript": "^4.4.2", "typescript": "4.6.x",
"zx": "^2.0.0" "zx": "^2.0.0"
}, },
"packageManager": "yarn@3.2.0", "packageManager": "yarn@3.2.0",

View file

@ -1,20 +1,19 @@
import {readdirSync, readFileSync, statSync, writeFileSync} from 'fs'
import {writeFile, readFile} from 'fs/promises'
import path from 'path'
import type {BuildOptions} from 'esbuild' import type {BuildOptions} from 'esbuild'
import esbuild from 'esbuild' import esbuild from 'esbuild'
import {definedGlobals} from '../../../theatre/devEnv/definedGlobals' import {readdir, readFile, stat, writeFile} from 'fs/promises'
import {mapValues} from 'lodash-es' import {mapValues} from 'lodash-es'
import path from 'path'
import React from 'react' import React from 'react'
import {renderToStaticMarkup} from 'react-dom/server' import {renderToStaticMarkup} from 'react-dom/server'
import {ServerStyleSheet} from 'styled-components' import {ServerStyleSheet} from 'styled-components'
import {PlaygroundPage} from './home/PlaygroundPage' import {definedGlobals} from '../../../theatre/devEnv/definedGlobals'
import {timer} from './timer'
import {openForOS} from './openForOS'
import {tryMultiplePorts} from './tryMultiplePorts'
import {createProxyServer} from './createProxyServer'
import {createEsbuildLiveReloadTools} from './createEsbuildLiveReloadTools' import {createEsbuildLiveReloadTools} from './createEsbuildLiveReloadTools'
import {createProxyServer} from './createProxyServer'
import {createServerForceClose} from './createServerForceClose' import {createServerForceClose} from './createServerForceClose'
import {PlaygroundPage} from './home/PlaygroundPage'
import {openForOS} from './openForOS'
import {timer} from './timer'
import {tryMultiplePorts} from './tryMultiplePorts'
const playgroundDir = (folder: string) => path.join(__dirname, '..', folder) const playgroundDir = (folder: string) => path.join(__dirname, '..', folder)
const buildDir = playgroundDir('build') const buildDir = playgroundDir('build')
@ -24,16 +23,20 @@ const personalDir = playgroundDir('src/personal')
const testDir = playgroundDir('src/tests') const testDir = playgroundDir('src/tests')
export async function start(options: { export async function start(options: {
/** enable live reload and watching stuff */
dev: boolean dev: boolean
findAvailablePort: boolean serve?: {
openBrowser: boolean findAvailablePort: boolean
waitBeforeStartingServer?: Promise<void> openBrowser: boolean
/** defaults to 8080 */ waitBeforeStartingServer?: Promise<void>
defaultPort?: number /** defaults to 8080 */
defaultPort?: number
}
}): Promise<{stop(): Promise<void>}> { }): Promise<{stop(): Promise<void>}> {
const defaultPort = options.defaultPort ?? 8080 const defaultPort = options.serve?.defaultPort ?? 8080
const liveReload = options.dev ? createEsbuildLiveReloadTools() : undefined const liveReload =
options.serve && options.dev ? createEsbuildLiveReloadTools() : undefined
type PlaygroundExample = { type PlaygroundExample = {
useHtml?: string useHtml?: string
@ -48,62 +51,72 @@ export async function start(options: {
} }
// Collect all entry directories per module per group // Collect all entry directories per module per group
const groups: Groups = Object.fromEntries( const groups: Groups = await Promise.all(
[sharedDir, personalDir, testDir] [sharedDir, personalDir, testDir].map(async (groupDir) =>
.map((groupDir) => { readdir(groupDir)
try { .then(async (groupDirItems) => [
return [ path.basename(groupDir),
path.basename(groupDir), await Promise.all(
Object.fromEntries( groupDirItems.map(
readdirSync(groupDir) async (
.map( moduleDirName,
(moduleDirName): [string, PlaygroundExample | undefined] => { ): Promise<[string, PlaygroundExample | undefined]> => {
const entryKey = path.basename(moduleDirName) const entryKey = path.basename(moduleDirName)
const entryFilePath = path.join( const entryFilePath = path.join(
groupDir, groupDir,
moduleDirName, moduleDirName,
'index.tsx', 'index.tsx',
)
if (!tryOrUndefined(() => statSync(entryFilePath).isFile()))
return [entryKey, undefined]
return [
entryKey,
{
// Including your own html file for playground is an experimental feature,
// it's not quite ready for "prime time" and advertising to the masses until
// it properly handles file watching.
// It's good for now, since we can use it for some demos, just make sure that
// you add a comment to the custom index.html file saying that you have to
// restart playground server entirely to see changes.
useHtml: tryOrUndefined(() =>
readFileSync(
path.join(groupDir, moduleDirName, 'index.html'),
'utf-8',
),
),
entryFilePath,
outDir: path.join(
buildDir,
path.basename(groupDir),
moduleDirName,
),
},
]
},
) )
.filter((entry) => entry[1] !== undefined),
if (
!(await stat(entryFilePath)
.then((s) => s.isFile())
.catch(() => false))
)
return [entryKey, undefined]
return [
entryKey,
{
// Including your own html file for playground is an experimental feature,
// it's not quite ready for "prime time" and advertising to the masses until
// it properly handles file watching.
// It's good for now, since we can use it for some demos, just make sure that
// you add a comment to the custom index.html file saying that you have to
// restart playground server entirely to see changes.
useHtml: await readFile(
path.join(groupDir, moduleDirName, 'index.html'),
'utf-8',
).catch(() => undefined),
entryFilePath,
outDir: path.join(
buildDir,
path.basename(groupDir),
moduleDirName,
),
},
]
},
), ),
] ).then((entries) =>
} catch (e) { Object.fromEntries(
entries.filter((entry) => entry[1] !== undefined),
),
),
])
.catch(() =>
// If the group dir doesn't exist, we just set its entry to undefined // If the group dir doesn't exist, we just set its entry to undefined
return [path.basename(groupDir), undefined] [path.basename(groupDir), undefined],
} ),
}) ),
// and then filter it out.
.filter((entry) => entry[1] !== undefined),
) )
.then((entries) =>
Object.fromEntries(
// and then filter it out.
entries.filter((entry) => entry[1] !== undefined),
),
)
.catch(wrapCatch('reading group dirs'))
// Collect all entry files // Collect all entry files
const entryPoints = Object.values(groups) const entryPoints = Object.values(groups)
@ -174,7 +187,7 @@ export async function start(options: {
{ {
filter: /index\.tsx?$/, filter: /index\.tsx?$/,
}, },
(loadFile) => { async (loadFile) => {
const indexHtmlPath = loadFile.path.replace( const indexHtmlPath = loadFile.path.replace(
/index\.tsx?$/, /index\.tsx?$/,
'index.html', 'index.html',
@ -182,11 +195,11 @@ export async function start(options: {
const relToSrc = path.relative(srcDir, indexHtmlPath) const relToSrc = path.relative(srcDir, indexHtmlPath)
const isInSrcFolder = !relToSrc.startsWith('..') const isInSrcFolder = !relToSrc.startsWith('..')
if (isInSrcFolder) { if (isInSrcFolder) {
const newHtml = tryOrUndefined(() => const newHtml = await readFile(indexHtmlPath, 'utf-8').catch(
readFileSync(indexHtmlPath, 'utf-8'), () => undefined,
) )
if (newHtml) { if (newHtml) {
writeFileSync( await writeFile(
path.resolve(buildDir, relToSrc), path.resolve(buildDir, relToSrc),
newHtml.replace( newHtml.replace(
/<\/body>/, /<\/body>/,
@ -196,6 +209,10 @@ export async function start(options: {
'../index.js', '../index.js',
)}"></script></body>`, )}"></script></body>`,
), ),
).catch(
wrapCatch(
`loading index.tsx creates corresponding index.html for ${relToSrc}`,
),
) )
} }
@ -215,14 +232,19 @@ export async function start(options: {
await esbuild await esbuild
.build(esbuildConfig) .build(esbuildConfig)
.finally(() => _initialBuild.stop()) .finally(() => _initialBuild.stop())
.catch((err) => { .catch(
// if in dev mode, permit continuing to watch even if there was an error // if in dev mode, permit continuing to watch even if there was an error
return options.dev ? Promise.resolve() : Promise.reject(err) options.dev
}) ? () => Promise.resolve()
: wrapCatch(`failed initial esbuild.build`),
)
.then(async (buildResult) => { .then(async (buildResult) => {
esbuildWatchStop = buildResult?.stop esbuildWatchStop = buildResult?.stop
// Read index.html template // Read index.html template
const index = await readFile(path.join(__dirname, 'index.html'), 'utf8') const index = await readFile(
path.join(__dirname, 'index.html'),
'utf8',
).catch(wrapCatch('reading index.html template'))
await Promise.all([ await Promise.all([
// Write home page // Write home page
writeFile( writeFile(
@ -231,7 +253,7 @@ export async function start(options: {
.replace(/<\/head>/, `${homeHtml.head}<\/head>`) .replace(/<\/head>/, `${homeHtml.head}<\/head>`)
.replace(/<body>/, `<body>${homeHtml.html}`), .replace(/<body>/, `<body>${homeHtml.html}`),
'utf-8', 'utf-8',
), ).catch(wrapCatch('writing build index.html')),
// Write module pages // Write module pages
...outModules.map((outModule) => ...outModules.map((outModule) =>
writeFile( writeFile(
@ -246,17 +268,24 @@ export async function start(options: {
)}"></script></body>`, )}"></script></body>`,
), ),
'utf-8', 'utf-8',
).catch(
wrapCatch(
`writing index.html for ${path.relative(
buildDir,
outModule.outDir,
)}`,
),
), ),
), ),
]) ])
}) })
.catch((err) => { .catch((err) => {
console.error(err) console.error('build.ts: esbuild or html files writing error', err)
return process.exit(1) return process.exit(1)
}) })
// Only start dev server in dev, otherwise just run build and that's it // Only start dev server in serve, otherwise just run build and that's it
if (!options.dev) { if (!options.serve) {
return { return {
stop() { stop() {
esbuildWatchStop?.() esbuildWatchStop?.()
@ -265,7 +294,8 @@ export async function start(options: {
} }
} }
await options.waitBeforeStartingServer const {serve} = options
await serve.waitBeforeStartingServer
// We start ESBuild serve with no build config because it doesn't need to build // We start ESBuild serve with no build config because it doesn't need to build
// anything, we are already using ESBuild watch. // anything, we are already using ESBuild watch.
@ -278,14 +308,14 @@ export async function start(options: {
}) })
const proxyForceExit = createServerForceClose(proxyServer) const proxyForceExit = createServerForceClose(proxyServer)
const portTries = options.findAvailablePort ? 10 : 1 const portTries = serve.findAvailablePort ? 10 : 1
const portChosen = await tryMultiplePorts(defaultPort, portTries, proxyServer) const portChosen = await tryMultiplePorts(defaultPort, portTries, proxyServer)
const hostedAt = `http://localhost:${portChosen}` const hostedAt = `http://localhost:${portChosen}`
console.log('Playground running at', hostedAt) console.log('Playground running at', hostedAt)
if (options.openBrowser) { if (serve.openBrowser) {
setTimeout(() => { setTimeout(() => {
if (!liveReload?.hasOpenConnections()) openForOS(hostedAt) if (!liveReload?.hasOpenConnections()) openForOS(hostedAt)
}, 1000) }, 1000)
@ -302,10 +332,8 @@ export async function start(options: {
} }
} }
function tryOrUndefined<T>(fn: () => T): T | undefined { function wrapCatch(message: string) {
try { return (err: any) => {
return fn() return Promise.reject(`Rejected "${message}":\n ${err.toString()}`)
} catch (err) {
return undefined
} }
} }

View file

@ -1,9 +1,17 @@
// @ts-check
const {timer} = require('./timer') const {timer} = require('./timer')
const dev = process.argv.find((arg) => ['--dev', '-d'].includes(arg)) != null const dev = process.argv.find((arg) => ['--dev', '-d'].includes(arg)) != null
const serve =
process.argv.find((arg) => ['--serve'].includes(arg)) != null || undefined
const isCI = Boolean(process.env.CI) const isCI = Boolean(process.env.CI)
/** Currently running server that can be stopped before restarting */
let current let current
console.log('cli.js', {dev, serve, isCI})
function onUpdatedBuildScript(rebuild) { function onUpdatedBuildScript(rebuild) {
delete require.cache[require.resolve('./build.compiled')] delete require.cache[require.resolve('./build.compiled')]
/** @type {import("./build")} */ /** @type {import("./build")} */
@ -12,11 +20,13 @@ function onUpdatedBuildScript(rebuild) {
try { try {
module module
.start({ .start({
dev, dev: !isCI && dev,
findAvailablePort: !isCI, serve: serve && {
// If not in CI, try to spawn a browser findAvailablePort: !isCI,
openBrowser: !isCI && !rebuild, // If not in CI, try to spawn a browser
waitBeforeStartingServer: current?.stop(), openBrowser: !isCI && !rebuild,
waitBeforeStartingServer: current?.stop(),
},
}) })
.then((running) => { .then((running) => {
current = running current = running

View file

@ -68,7 +68,7 @@ const config: PlaywrightTestConfig = {
TODO 👆 TODO 👆
*/ */
webServer: { webServer: {
command: 'yarn run serve', command: 'yarn run serve:ci',
port: 8080, port: 8080,
reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
}, },

View file

@ -8,10 +8,11 @@
"dist/**/*" "dist/**/*"
], ],
"scripts": { "scripts": {
"serve": "node devEnv/cli.js --dev", "serve": "node devEnv/cli.js --serve --dev",
"serve:ci": "node devEnv/cli.js --serve",
"build": "node devEnv/cli.js", "build": "node devEnv/cli.js",
"build:static": "yarn build", "build:static": "echo 'building for vercel' && yarn run build",
"typecheck": "yarn run build", "typecheck": "tsc --noEmit",
"test": "playwright test --config=devEnv/playwright.config.ts", "test": "playwright test --config=devEnv/playwright.config.ts",
"test:ci": "percy exec -- playwright test --reporter=dot --config=devEnv/playwright.config.ts --project=chromium" "test:ci": "percy exec -- playwright test --reporter=dot --config=devEnv/playwright.config.ts --project=chromium"
}, },
@ -28,6 +29,7 @@
"@types/lodash-es": "^4.17.4", "@types/lodash-es": "^4.17.4",
"@types/node": "^15.6.2", "@types/node": "^15.6.2",
"@types/react": "^17.0.9", "@types/react": "^17.0.9",
"esbuild": "^0.13.15",
"three": "^0.130.1", "three": "^0.130.1",
"typescript": "^4.4.2" "typescript": "^4.4.2"
} }

3957
yarn.lock

File diff suppressed because it is too large Load diff