Add tests for Theatre.js + popular setups in the ecosystem (#165)

This implements some basic infra for testing Theatre.js with popular setups such as npm/yarn/pnpm, webpack/vite/parcel, js/ts, etc.

So far, the only existing setup has been with create-react-app. Will add more in the future.

Co-authored-by: Fülöp <fulopkovacs@users.noreply.github.com>
Co-authored-by: Aria Minaei <aria.minaei@gmail.com>
This commit is contained in:
Fülöp 2022-05-17 20:53:01 +02:00 committed by GitHub
parent 2324218453
commit 3d10325873
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 13811 additions and 55 deletions

View file

@ -39,7 +39,7 @@ module.exports = {
}, },
], ],
}, },
ignorePatterns: ['*.d.ts', '*.ignore.ts'], ignorePatterns: ['*.d.ts', '*.ignore.ts', 'ecosystem-tests/*'],
overrides: [ overrides: [
{ {
files: ['*.ts', '*.tsx'], files: ['*.ts', '*.tsx'],

View file

@ -28,7 +28,13 @@ jobs:
- uses: actions/cache@v2 - uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} # Note that we assume the following things about the ecosystem tests here:
# 1. Every directory in `build_test` is a project managed with yarn
# 2. All these projects store their cache in `<project>/.yarn/cache`
# It's not that robust, but should be sufficient for us for now.
path: |
${{ steps.yarn-cache-dir-path.outputs.dir }}
${{ github.workspace }}/ecosystem-tests/*/.yarn/cache
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: | restore-keys: |
${{ runner.os }}-yarn- ${{ runner.os }}-yarn-
@ -37,6 +43,8 @@ jobs:
- run: yarn typecheck - run: yarn typecheck
- run: yarn lint:all --max-warnings 0 - run: yarn lint:all --max-warnings 0
- run: yarn test - run: yarn test
- run: yarn build
- run: yarn test:ecosystem
- name: Download playwright - name: Download playwright
run: yarn workspace playground run playwright install run: yarn workspace playground run playwright install
- name: Run e2e tests with percy - name: Run e2e tests with percy

9
.gitignore vendored
View file

@ -19,3 +19,12 @@
!.yarn/sdks !.yarn/sdks
!.yarn/versions !.yarn/versions
# Required by the `parcel_v2`
# ecosystem tests
.parcel-cache
yalc.lock
/ecosystem-tests/*/.yalc
/ecosystem-tests/*/.yarn
/ecosystem-tests/*/build

View file

@ -1,4 +1,7 @@
if (process.env.THEATRE_IS_PUBLISHING !== 'true') { if (
process.env.THEATRE_IS_PUBLISHING !== 'true' &&
process.env.USING_YALC !== 'true'
) {
throw Error( throw Error(
`This script may run only when the "release" command in monorepo's root is running.`, `This script may run only when the "release" command in monorepo's root is running.`,
) )

110
ecosystem-tests/README.md Normal file
View file

@ -0,0 +1,110 @@
# Ecosystem tests
Inspired by the
[#help channel on our Discord](https://discord.com/channels/870988717190426644/870988717190426647)
we collect examples for including `Theatre.js` in project that use different
tools (`parcel`, `Next.js`, `vanilla Rollup`, etc...) to build them in the CI
(these are the _ecosystem tests_).
## The currently tested setups
| setup | tools | `package.json` |
| ---------------------------- | ----------------------------------- | ------------------------------------- |
| ecosystem-tests/create-react-app | `create-react-app`, `r3f extension` | [link](create-react-app/package.json) |
## Testing the configurations locally
```sh
# clean existing build artifacts
yarn clean
# this will build all packages, publish them to yalc, link them to each setup, and run the `yarn build` command on that setup
yarn test:ecosystem
```
After running the above, you can also start the dev server of each setup to try it manually:
```sh
cd ecosystem-tests/[setup-name]
yarn start
```
## Adding a new setup
If you wish to test a new setup (say Vite, or cool-new-bundler), here is how to
do it:
1. Build the monorepo packages and publish them to the local npm registry,
[yalc](https://github.com/wclr/yalc).
```sh
cd /path/to/theatre-monorepo
# build all the packages
yarn build
# publish them to yalc (the local npm registry)
zx scripts/publish-to-yalc.mjs
```
1. Start your new setup in a directory outside of the monorepo
```sh
# start a project outside the monorepo we'll copy it in later
cd /path/---------outside---------/theatre-monorepo
# make a new setup folder
mkdir new-setup .
cd new-setup
```
1. Bootstrap your setup using npm, yarn, or other bootstrapping scripts (like
`npx create-react-app`)
```sh
npm init --yes
```
1. Make sure there is a `yarn.lock` or `package-lock.json` file in this
directory. Otherwise, when we move it back into the monorepo, yarn will
complain that this package is not listed in the monorepo as a workspace.
```sh
touch yarn.lock
```
1. Copy the new directory back to the monorepo and `cd` into it
```sh
cp -r ./path/---------outside---------/theatre-monorepo/new-setup /path/to/theatre/monorepo/build-tests/new-setup
cd /path/to/theatre/monorepo/build-tests/new-setup
```
1. Let yarn/npm run an install
```sh
yarn install
```
1. Install `@theatre/core` and `@theatre/studio`, and possibly `@theatre/r3f`
from the local registry:
```sh
npx yalc link @theatre/core @theatre/studio @theatre/r3f
```
1. Copy the source (`src/*`) of one of the other setups into `new-setup` so you
don't have to start from scratch.
1. Make sure that you add a `yarn build` script to `new-setup/package.json`,
because it
[will be used](https://github.com/theatre-js/theatre/blob/db7dadc0c997316f2027736e2ecba0ea4acda2d4/scripts/build-tests/build-setups.mjs#L18)
to build the setup in the CI.
1. Test your setup by running its dev server or doing a build
```sh
yarn start
```
> **Gotchas**
> Some bundlers like webpack are not configured to work well with yarn workspaces by default. For example, the webpack config of create-react-app, tries to look up the node_modules chain to find missing dependencies, which is not a behavior that we want in build-tests setups. So if a setup doesn't work, try running it outside the monorepo to see if being in the monorepo is what's causing it to fail.
Feel free to check out [the existing setups](#the-currently-tested-setups) in
`ecosystem-tests` if you get stuck.

View file

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View file

@ -0,0 +1,16 @@
# Theatre.js project with `create-react-app` using the `r3f extension`
## Setup
Please refer to [this guide](../README.md#testing-the-configurations-locally).
## Usage
- start the development server:
```sh
yarn start
```
- build the project:
```sh
yarn build
```

View file

@ -0,0 +1,38 @@
{
"name": "@theatre-build-tests/react-app",
"version": "0.1.0",
"private": true,
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"dependencies": {
"@react-three/drei": "^7.2.2",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"react-scripts": "^5.0.1",
"three": "^0.130.1",
"web-vitals": "^1.0.1"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View file

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View file

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View file

@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View file

@ -0,0 +1,150 @@
import {getProject} from '@theatre/core'
import * as THREE from 'three'
import {useState, useEffect, useRef} from 'react'
import {useFrame, Canvas} from '@react-three/fiber'
import {Shadow, softShadows} from '@react-three/drei'
import React from 'react'
import studio from '@theatre/studio'
import {editable as e, SheetProvider, extension} from '@theatre/r3f'
if (process.env.NODE_ENV === 'development') {
studio.extend(extension)
studio.initialize()
}
// Soft shadows are expensive, comment and refresh when it's too slow
softShadows()
// credit: https://codesandbox.io/s/camera-pan-nsb7f
function Button() {
const vec = new THREE.Vector3()
const light = useRef(undefined)
const [active, setActive] = useState(false)
const [zoom, set] = useState(true)
useEffect(
() => void (document.body.style.cursor = active ? 'pointer' : 'auto'),
[active],
)
useFrame((state) => {
const step = 0.1
const camera = state.camera
camera.fov = THREE.MathUtils.lerp(camera.fov, zoom ? 10 : 42, step)
camera.position.lerp(
vec.set(zoom ? 25 : 10, zoom ? 1 : 5, zoom ? 0 : 10),
step,
)
state.camera.lookAt(0, 0, 0)
state.camera.updateProjectionMatrix()
light.current.position.lerp(
vec.set(zoom ? 4 : 0, zoom ? 3 : 8, zoom ? 3 : 5),
step,
)
})
return (
<e.mesh
receiveShadow
castShadow
onClick={() => set(!zoom)}
onPointerOver={() => setActive(true)}
onPointerOut={() => setActive(false)}
uniqueName="The Button"
>
<sphereBufferGeometry args={[0.75, 64, 64]} />
<meshPhysicalMaterial
color={active ? 'purple' : '#e7b056'}
clearcoat={1}
clearcoatRoughness={0}
/>
<Shadow
position-y={-0.79}
rotation-x={-Math.PI / 2}
opacity={0.6}
scale={[0.8, 0.8, 1]}
/>
<directionalLight
ref={light}
castShadow
intensity={1.5}
shadow-camera-far={70}
/>
</e.mesh>
)
}
function Plane({color, uniqueName, ...props}) {
return (
<e.mesh receiveShadow castShadow {...props} uniqueName={uniqueName}>
<boxBufferGeometry />
<meshStandardMaterial color={color} />
</e.mesh>
)
}
const sheet = getProject('Playground - R3F').sheet('R3F-Canvas')
function App() {
return (
<div>
<Canvas
// @ts-ignore
shadowMap
>
<SheetProvider sheet={sheet}>
{/* @ts-ignore */}
<e.perspectiveCamera makeDefault uniqueName="Camera" />
<ambientLight intensity={0.4} />
<e.pointLight
position={[-10, -10, 5]}
intensity={2}
color="#ff20f0"
uniqueName="Light 1"
/>
<e.pointLight
position={[0, 0.5, -1]}
distance={1}
intensity={2}
color="#e4be00"
uniqueName="Light 2"
/>
<group position={[0, -0.9, -3]}>
<Plane
color="hotpink"
rotation-x={-Math.PI / 2}
position-z={2}
scale={[4, 20, 0.2]}
uniqueName="plane1"
/>
<Plane
color="#e4be00"
rotation-x={-Math.PI / 2}
position-y={1}
scale={[4.2, 0.2, 4]}
uniqueName="plane2"
/>
<Plane
color="#736fbd"
rotation-x={-Math.PI / 2}
position={[-1.7, 1, 3.5]}
scale={[0.5, 4, 4]}
uniqueName="plane3"
/>
<Plane
color="white"
rotation-x={-Math.PI / 2}
position={[0, 4.5, 3]}
scale={[2, 0.03, 4]}
uniqueName="plane4"
/>
</group>
<Button />
</SheetProvider>
</Canvas>
</div>
)
}
export default App

View file

@ -0,0 +1,7 @@
import {render, screen} from '@testing-library/react'
test('renders learn react link', () => {
render(<App />)
const linkElement = screen.getByText(/learn react/i)
expect(linkElement).toBeInTheDocument()
})

View file

@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View file

@ -0,0 +1,19 @@
import ReactDOM from 'react-dom'
import './index.css'
import reportWebVitals from './reportWebVitals'
import '@theatre/studio'
import {getProject} from '@theatre/core'
import React from 'react'
import App from './App'
ReactDOM.render(
<React.StrictMode>
<App project={getProject('CRA project')} />
</React.StrictMode>,
document.getElementById('root'),
)
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals()

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1 @@
/// <reference types="react-scripts" />

View file

@ -0,0 +1,13 @@
const reportWebVitals = (onPerfEntry) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({getCLS, getFID, getFCP, getLCP, getTTFB}) => {
getCLS(onPerfEntry)
getFID(onPerfEntry)
getFCP(onPerfEntry)
getLCP(onPerfEntry)
getTTFB(onPerfEntry)
})
}
}
export default reportWebVitals

View file

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom'

View file

@ -0,0 +1,130 @@
import {useLayoutEffect, useRef} from 'react'
const noop = () => {}
function createCursorLock(cursor) {
const el = document.createElement('div')
el.style.cssText = `
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 9999999;`
el.style.cursor = cursor
document.body.appendChild(el)
const relinquish = () => {
document.body.removeChild(el)
}
return relinquish
}
export default function useDrag(target, opts) {
const optsRef = useRef(opts)
optsRef.current = opts
const modeRef = useRef('notDragging')
const stateRef = useRef({dragHappened: false, startPos: {x: 0, y: 0}})
useLayoutEffect(() => {
if (!target) return
const getDistances = (event) => {
const {startPos} = stateRef.current
return [event.screenX - startPos.x, event.screenY - startPos.y]
}
let relinquishCursorLock = noop
const dragHandler = (event) => {
if (!stateRef.current.dragHappened && optsRef.current.lockCursorTo) {
relinquishCursorLock = createCursorLock(optsRef.current.lockCursorTo)
}
if (!stateRef.current.dragHappened) stateRef.current.dragHappened = true
modeRef.current = 'dragging'
const deltas = getDistances(event)
optsRef.current.onDrag(deltas[0], deltas[1], event)
}
const dragEndHandler = () => {
removeDragListeners()
modeRef.current = 'notDragging'
optsRef.current.onDragEnd &&
optsRef.current.onDragEnd(stateRef.current.dragHappened)
relinquishCursorLock()
relinquishCursorLock = noop
}
const addDragListeners = () => {
document.addEventListener('mousemove', dragHandler)
document.addEventListener('mouseup', dragEndHandler)
}
const removeDragListeners = () => {
document.removeEventListener('mousemove', dragHandler)
document.removeEventListener('mouseup', dragEndHandler)
}
const preventUnwantedClick = (event) => {
if (optsRef.current.disabled) return
if (stateRef.current.dragHappened) {
if (
!optsRef.current.dontBlockMouseDown &&
modeRef.current !== 'notDragging'
) {
event.stopPropagation()
event.preventDefault()
}
stateRef.current.dragHappened = false
}
}
const dragStartHandler = (event) => {
const opts = optsRef.current
if (opts.disabled === true) return
if (event.button !== 0) return
const resultOfStart = opts.onDragStart && opts.onDragStart(event)
if (resultOfStart === false) return
if (!opts.dontBlockMouseDown) {
event.stopPropagation()
event.preventDefault()
}
modeRef.current = 'dragStartCalled'
const {screenX, screenY} = event
stateRef.current.startPos = {x: screenX, y: screenY}
stateRef.current.dragHappened = false
addDragListeners()
}
const onMouseDown = (e) => {
dragStartHandler(e)
}
target.addEventListener('mousedown', onMouseDown)
target.addEventListener('click', preventUnwantedClick)
return () => {
removeDragListeners()
target.removeEventListener('mousedown', onMouseDown)
target.removeEventListener('click', preventUnwantedClick)
relinquishCursorLock()
if (modeRef.current !== 'notDragging') {
optsRef.current.onDragEnd &&
optsRef.current.onDragEnd(modeRef.current === 'dragging')
}
modeRef.current = 'notDragging'
}
}, [target])
}

File diff suppressed because it is too large Load diff

View file

@ -11,8 +11,10 @@
"playground": "yarn workspace playground run serve", "playground": "yarn workspace playground run serve",
"test:e2e": "yarn workspace playground run test", "test:e2e": "yarn workspace playground run test",
"test:e2e:ci": "yarn workspace playground run test:ci", "test:e2e:ci": "yarn workspace playground run test:ci",
"test:ecosystem": "zx scripts/ecosystem-tests/ci.mjs",
"typecheck": "yarn run build:ts", "typecheck": "yarn run build:ts",
"build": "zx scripts/build.mjs", "build": "zx scripts/build.mjs",
"clean": "zx scripts/clean.mjs",
"build:ts": "tsc --build ./devEnv/typecheck-all-projects/tsconfig.all.json", "build:ts": "tsc --build ./devEnv/typecheck-all-projects/tsconfig.all.json",
"test": "jest", "test": "jest",
"postinstall": "husky install", "postinstall": "husky install",
@ -54,7 +56,12 @@
"node-gyp": "^8.1.0", "node-gyp": "^8.1.0",
"prettier": "^2.3.2", "prettier": "^2.3.2",
"typescript": "^4.4.2", "typescript": "^4.4.2",
"yalc": "^1.0.0-pre.53",
"zx": "^2.0.0" "zx": "^2.0.0"
}, },
"packageManager": "yarn@3.2.0" "packageManager": "yarn@3.2.0",
"resolutions": {
"@types/react": "^17.0.9",
"@types/react-dom": "^17.0.9"
}
} }

View file

@ -16,8 +16,10 @@
"dist/**/*" "dist/**/*"
], ],
"scripts": { "scripts": {
"build": "node -r esbuild-register ./devEnv/build.ts", "build": "yarn run build:js",
"prepublish": "node ../../devEnv/ensurePublishing.js" "build:js": "node -r esbuild-register ./devEnv/build.ts",
"prepublish": "node ../../devEnv/ensurePublishing.js",
"clean": "rm -rf ./dist && rm -f tsconfig.tsbuildinfo"
}, },
"devDependencies": { "devDependencies": {
"esbuild": "^0.12.15", "esbuild": "^0.12.15",

View file

@ -24,7 +24,8 @@
"build:ts": "tsc --build ./tsconfig.json", "build:ts": "tsc --build ./tsconfig.json",
"build:js": "node -r esbuild-register ./devEnv/build.ts", "build:js": "node -r esbuild-register ./devEnv/build.ts",
"build:api-json": "api-extractor run --local --config devEnv/api-extractor.json", "build:api-json": "api-extractor run --local --config devEnv/api-extractor.json",
"prepublish": "node ../../devEnv/ensurePublishing.js" "prepublish": "node ../../devEnv/ensurePublishing.js",
"clean": "rm -rf ./dist && rm -f tsconfig.tsbuildinfo"
}, },
"devDependencies": { "devDependencies": {
"@microsoft/api-extractor": "^7.18.11", "@microsoft/api-extractor": "^7.18.11",

View file

@ -19,9 +19,9 @@
"url": "https://github.com/AriaMinaei/theatre", "url": "https://github.com/AriaMinaei/theatre",
"directory": "packages/r3f" "directory": "packages/r3f"
}, },
"main": "dist/esm/index.js", "main": "dist/index.js",
"module": "dist/esm/index.js", "module": "dist/index.js",
"types": "dist/esm/index.d.ts", "types": "dist/index.d.ts",
"sideEffects": false, "sideEffects": false,
"files": [ "files": [
"dist/**/*" "dist/**/*"
@ -30,7 +30,8 @@
"prepack": "yarn run build", "prepack": "yarn run build",
"typecheck": "yarn run build", "typecheck": "yarn run build",
"build": "tsc --build ./tsconfig.json", "build": "tsc --build ./tsconfig.json",
"prepublish": "yarn run build" "prepublish": "yarn run build",
"clean": "rm -rf ./dist && rm -f tsconfig.tsbuildinfo"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^26.0.23", "@types/jest": "^26.0.23",

View file

@ -1,7 +1,7 @@
{ {
"extends": "../../tsconfig.base.json", "extends": "../../tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"outDir": "dist/esm", "outDir": "dist",
"lib": ["ESNext", "DOM"], "lib": ["ESNext", "DOM"],
"rootDir": "src", "rootDir": "src",
"types": ["jest", "node"], "types": ["jest", "node"],

View file

@ -24,7 +24,8 @@
"build:ts": "tsc --build ./tsconfig.json", "build:ts": "tsc --build ./tsconfig.json",
"build:js": "node -r esbuild-register ./devEnv/build.ts", "build:js": "node -r esbuild-register ./devEnv/build.ts",
"build:api-json": "api-extractor run --local --config devEnv/api-extractor.json", "build:api-json": "api-extractor run --local --config devEnv/api-extractor.json",
"prepublish": "node ../../devEnv/ensurePublishing.js" "prepublish": "node ../../devEnv/ensurePublishing.js",
"clean": "rm -rf ./dist && rm -f tsconfig.tsbuildinfo"
}, },
"devDependencies": { "devDependencies": {
"@microsoft/api-extractor": "^7.18.11", "@microsoft/api-extractor": "^7.18.11",

View file

@ -9,7 +9,6 @@ const packagesToBuild = [
'@theatre/browser-bundles', '@theatre/browser-bundles',
'@theatre/r3f', '@theatre/r3f',
] ]
;(async function () { ;(async function () {
// better quote function from https://github.com/google/zx/pull/167 // better quote function from https://github.com/google/zx/pull/167
$.quote = function quote(arg) { $.quote = function quote(arg) {

37
scripts/clean.mjs Normal file
View file

@ -0,0 +1,37 @@
/**
* cleans the build artifacts of all packages
*/
const packages = [
'theatre',
'@theatre/dataverse',
'@theatre/react',
'@theatre/browser-bundles',
'@theatre/r3f',
]
;(async function () {
// 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`),
])
})()

View file

@ -0,0 +1,34 @@
/**
* Build the test setups
*/
import path from 'path'
import {colorize, getEcosystemTestSetups} from './utils.mjs'
const root = path.resolve(__dirname, '../..')
const absPathOfEcosystemTestSetups = getEcosystemTestSetups(root)
const setupsWithErros = []
// Try building the setups
;(async function () {
for (const setupDir of absPathOfEcosystemTestSetups) {
try {
cd(setupDir)
await $`yarn build`
} catch (err) {
console.error(err)
setupsWithErros.push(setupDir)
}
}
// Stop if there were any errors during the build process,
// and print all of them to the console.
if (setupsWithErros.length !== 0) {
throw new Error(
`The following setups had problems when their dependencies were being installed:\n${colorize.red(
setupsWithErros.join('\n'),
)}`,
)
}
})()

View file

@ -0,0 +1,6 @@
;(async function () {
await $`zx scripts/publish-to-yalc.mjs`
await $`zx scripts/ecosystem-tests/install-dependencies.mjs`
await $`zx scripts/ecosystem-tests/link-theatre-packages.mjs`
await $`zx scripts/ecosystem-tests/build-setups.mjs`
})()

View file

@ -0,0 +1,35 @@
/**
* Install the dependencies of the ecosystem test setups
*/
import path from 'path'
import {colorize, getEcosystemTestSetups} from './utils.mjs'
const root = path.resolve(__dirname, '../..')
const absPathOfTestSetups = getEcosystemTestSetups(root)
const setupsWithErrors = []
// Try installing the dependencies of the test setups
;(async function () {
for (const dirOfSetup of absPathOfTestSetups) {
try {
cd(dirOfSetup)
await $`yarn`
} catch (err) {
console.error(err)
setupsWithErrors.push(dirOfSetup)
}
}
// Stop if there were any errors during the linking process,
// and print all of them to the console.
if (setupsWithErrors.length !== 0) {
throw new Error(
`The following setups had problems when their dependencies were being installed:\n${colorize.red(
setupsWithErrors.join('\n'),
)}`,
)
}
})()

View file

@ -0,0 +1,46 @@
/**
* Add the theatre packages to the ecosystem test setups with `yalc link`
*/
import path from 'path'
import {colorize, getEcosystemTestSetups} from './utils.mjs'
const root = path.resolve(__dirname, '../..')
const absPathsOfSetups = getEcosystemTestSetups(root)
// The packages that should be linked with `yalc`
const packagesToLink = [
'@theatre/core',
'@theatre/studio',
'@theatre/dataverse',
'@theatre/react',
'@theatre/r3f',
]
const setupsWithErrors = []
;(async function () {
for (const setupDir of absPathsOfSetups) {
try {
cd(setupDir)
for (let pkg of packagesToLink) {
// Add the specified package to the setup's dependencies
// with `yalc link`.
await $`npx yalc link ${pkg}`
}
} catch (err) {
console.error(err)
setupsWithErrors.push(setupDir)
}
}
// Stop if there were any errors during the linking process,
// and print all of them to the console.
if (setupsWithErrors.length !== 0) {
throw new Error(
`The following setups had problems when their dependencies were being linked:\n${colorize.red(
setupsWithErrors.join('\n'),
)}`,
)
}
})()

View file

@ -0,0 +1,49 @@
/**
* Utility functions for the ecosystem tests
*/
import fs from 'fs'
import path from 'path'
/**
* Colorize a message
*/
export const colorize = {
/** @param {string} message */
red: (message) => '\x1b[31m' + message + '\x1b[0m',
/** @param {string} message */
green: (message) => '\x1b[32m' + message + '\x1b[0m',
/** @param {string} message */
yellow: (message) => '\x1b[33m' + message + '\x1b[0m',
}
/**
* Get all the setups from `./ecosystem-tests/`
*
* @param {string} root - Absolute path to the theatre monorepo
* @returns An array containing the absolute paths to the ecosystem test setups
*/
export function getEcosystemTestSetups(root) {
const buildTestsDir = path.join(root, 'ecosystem-tests')
let buildTestsDirEntries
try {
buildTestsDirEntries = fs.readdirSync(buildTestsDir)
} catch {
throw new Error(
`Could not list directory: "${buildTestsDir}" Is it an existing directory?`,
)
}
const setupsAbsPaths = []
// NOTE: We assume that every directory in `ecosystem-tests` is
// an ecosystem test setup!
for (const entry of buildTestsDirEntries) {
const entryAbsPath = path.join(buildTestsDir, entry)
if (fs.lstatSync(entryAbsPath).isDirectory()) {
setupsAbsPaths.push(entryAbsPath)
}
}
return setupsAbsPaths
}

View file

@ -0,0 +1,26 @@
/**
* Publish the theatre packages to a local registry with yalc for the ecosystem tests.
*/
import path from 'path'
const root = path.resolve(__dirname, '..')
// Make sure the script runs in the root of the monorepo
cd(root)
process.env.USING_YALC = 'true'
const packagesToPublish = [
'theatre/core',
'theatre/studio',
'packages/dataverse',
'packages/r3f',
'packages/react',
]
;(async function () {
// Publish the packages to the local `yalc` registry
for (const pkg of packagesToPublish) {
await $`npx yalc publish ${pkg}`
}
})()

View file

@ -187,6 +187,7 @@ const packagesWhoseVersionsShouldBump = [
) )
})() })()
/** @param {string} monorepoVersion */
async function assignVersions(monorepoVersion) { async function assignVersions(monorepoVersion) {
for (const packagePathRelativeFromRoot of packagesWhoseVersionsShouldBump) { for (const packagePathRelativeFromRoot of packagesWhoseVersionsShouldBump) {
const pathToPackage = path.resolve( const pathToPackage = path.resolve(

16
scripts/tsconfig.json Normal file
View file

@ -0,0 +1,16 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"noEmit": true,
"resolveJsonModule": true,
"types": [
"zx"
]
},
"include": [
"*.mjs",
"**/*.mjs",
]
}

View file

@ -16,7 +16,8 @@
"build:api-json": "run-p build:api-json:core build:api-json:studio", "build:api-json": "run-p build:api-json:core build:api-json:studio",
"build:api-json:core": "api-extractor run --local --config core/devEnv/api-extractor.json", "build:api-json:core": "api-extractor run --local --config core/devEnv/api-extractor.json",
"build:api-json:studio": "api-extractor run --local --config studio/devEnv/api-extractor.json", "build:api-json:studio": "api-extractor run --local --config studio/devEnv/api-extractor.json",
"build": "run-p build:ts build:js" "build": "run-p build:ts build:js",
"clean": "rm -rf ./.temp && rm -rf ./core/dist && rm -rf ./studio/dist"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.14.3", "@babel/cli": "^7.14.3",

121
yarn.lock
View file

@ -7163,15 +7163,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/react-dom@npm:^17.0.6":
version: 17.0.6
resolution: "@types/react-dom@npm:17.0.6"
dependencies:
"@types/react": "*"
checksum: cc167c24a77cef2be8dcabd1bbe8b95c0d713b943b2f1c8a1468c8009c50daad51a46859a217a8383b89bb4ea499937d405699ce8db164f70c871f9d22d703f0
languageName: node
linkType: hard
"@types/react-dom@npm:^17.0.9": "@types/react-dom@npm:^17.0.9":
version: 17.0.9 version: 17.0.9
resolution: "@types/react-dom@npm:17.0.9" resolution: "@types/react-dom@npm:17.0.9"
@ -7190,28 +7181,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/react@npm:*":
version: 17.0.3
resolution: "@types/react@npm:17.0.3"
dependencies:
"@types/prop-types": "*"
"@types/scheduler": "*"
csstype: ^3.0.2
checksum: d29f2a33ed69fae9d9f42f8b39b40352e3209eb821f827d566330adfdffb48d2dc730982356cd50a7fda6171e6bc7deca793f7b105c95fce615a45ab313ecf41
languageName: node
linkType: hard
"@types/react@npm:^17.0.19":
version: 17.0.19
resolution: "@types/react@npm:17.0.19"
dependencies:
"@types/prop-types": "*"
"@types/scheduler": "*"
csstype: ^3.0.2
checksum: d5af52623dd863e5a9f64eb72979f19c21e03012e25ee93adc93f664ed7976e81b654128dc653b225ad5ba2aaae24e0febe37face272bb051d6289e23ed7177d
languageName: node
linkType: hard
"@types/react@npm:^17.0.9": "@types/react@npm:^17.0.9":
version: 17.0.9 version: 17.0.9
resolution: "@types/react@npm:17.0.9" resolution: "@types/react@npm:17.0.9"
@ -12396,6 +12365,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"detect-indent@npm:^6.0.0":
version: 6.1.0
resolution: "detect-indent@npm:6.1.0"
checksum: ab953a73c72dbd4e8fc68e4ed4bfd92c97eb6c43734af3900add963fd3a9316f3bc0578b018b24198d4c31a358571eff5f0656e81a1f3b9ad5c547d58b2d093d
languageName: node
linkType: hard
"detect-newline@npm:^3.0.0": "detect-newline@npm:^3.0.0":
version: 3.1.0 version: 3.1.0
resolution: "detect-newline@npm:3.1.0" resolution: "detect-newline@npm:3.1.0"
@ -15063,7 +15039,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"fs-extra@npm:^8.1.0": "fs-extra@npm:^8.0.1, fs-extra@npm:^8.1.0":
version: 8.1.0 version: 8.1.0
resolution: "fs-extra@npm:8.1.0" resolution: "fs-extra@npm:8.1.0"
dependencies: dependencies:
@ -16232,6 +16208,15 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"ignore-walk@npm:^3.0.3":
version: 3.0.4
resolution: "ignore-walk@npm:3.0.4"
dependencies:
minimatch: ^3.0.4
checksum: 9e9c5ef6c3e0ed7ef5d797991abb554dbb7e60d5fedf6cf05c7129819689eba2b462f625c6e3561e0fc79841904eb829565513eeeab1b44f4fbec4d3146b1a8d
languageName: node
linkType: hard
"ignore@npm:^4.0.6": "ignore@npm:^4.0.6":
version: 4.0.6 version: 4.0.6
resolution: "ignore@npm:4.0.6" resolution: "ignore@npm:4.0.6"
@ -16239,6 +16224,13 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"ignore@npm:^5.0.4, ignore@npm:^5.1.8, ignore@npm:^5.2.0":
version: 5.2.0
resolution: "ignore@npm:5.2.0"
checksum: 6b1f926792d614f64c6c83da3a1f9c83f6196c2839aa41e1e32dd7b8d174cef2e329d75caabb62cb61ce9dc432f75e67d07d122a037312db7caa73166a1bdb77
languageName: node
linkType: hard
"ignore@npm:^5.1.4": "ignore@npm:^5.1.4":
version: 5.1.8 version: 5.1.8
resolution: "ignore@npm:5.1.8" resolution: "ignore@npm:5.1.8"
@ -16246,13 +16238,6 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"ignore@npm:^5.1.8, ignore@npm:^5.2.0":
version: 5.2.0
resolution: "ignore@npm:5.2.0"
checksum: 6b1f926792d614f64c6c83da3a1f9c83f6196c2839aa41e1e32dd7b8d174cef2e329d75caabb62cb61ce9dc432f75e67d07d122a037312db7caa73166a1bdb77
languageName: node
linkType: hard
"image-size@npm:^1.0.0": "image-size@npm:^1.0.0":
version: 1.0.1 version: 1.0.1
resolution: "image-size@npm:1.0.1" resolution: "image-size@npm:1.0.1"
@ -16420,6 +16405,13 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"ini@npm:^2.0.0":
version: 2.0.0
resolution: "ini@npm:2.0.0"
checksum: e7aadc5fb2e4aefc666d74ee2160c073995a4061556b1b5b4241ecb19ad609243b9cceafe91bae49c219519394bbd31512516cb22a3b1ca6e66d869e0447e84e
languageName: node
linkType: hard
"inline-style-prefixer@npm:^6.0.0": "inline-style-prefixer@npm:^6.0.0":
version: 6.0.0 version: 6.0.0
resolution: "inline-style-prefixer@npm:6.0.0" resolution: "inline-style-prefixer@npm:6.0.0"
@ -20676,6 +20668,36 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"npm-bundled@npm:^1.1.1":
version: 1.1.2
resolution: "npm-bundled@npm:1.1.2"
dependencies:
npm-normalize-package-bin: ^1.0.1
checksum: 6e599155ef28d0b498622f47f1ba189dfbae05095a1ed17cb3a5babf961e965dd5eab621f0ec6f0a98de774e5836b8f5a5ee639010d64f42850a74acec3d4d09
languageName: node
linkType: hard
"npm-normalize-package-bin@npm:^1.0.1":
version: 1.0.1
resolution: "npm-normalize-package-bin@npm:1.0.1"
checksum: ae7f15155a1e3ace2653f12ddd1ee8eaa3c84452fdfbf2f1943e1de264e4b079c86645e2c55931a51a0a498cba31f70022a5219d5665fbcb221e99e58bc70122
languageName: node
linkType: hard
"npm-packlist@npm:^2.1.5":
version: 2.2.2
resolution: "npm-packlist@npm:2.2.2"
dependencies:
glob: ^7.1.6
ignore-walk: ^3.0.3
npm-bundled: ^1.1.1
npm-normalize-package-bin: ^1.0.1
bin:
npm-packlist: bin/index.js
checksum: 799ce94b077e4dc366a9a5bcc5f006669263bb1a48d6948161aed915fd2f11dea8a7cf516a63fc78e5df059915591dade5928f0738baadc99a8ab4685d8b58c3
languageName: node
linkType: hard
"npm-run-all@npm:^4.1.5": "npm-run-all@npm:^4.1.5":
version: 4.1.5 version: 4.1.5
resolution: "npm-run-all@npm:4.1.5" resolution: "npm-run-all@npm:4.1.5"
@ -27633,6 +27655,7 @@ fsevents@^1.2.7:
node-gyp: ^8.1.0 node-gyp: ^8.1.0
prettier: ^2.3.2 prettier: ^2.3.2
typescript: ^4.4.2 typescript: ^4.4.2
yalc: ^1.0.0-pre.53
zx: ^2.0.0 zx: ^2.0.0
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -29900,6 +29923,24 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"yalc@npm:^1.0.0-pre.53":
version: 1.0.0-pre.53
resolution: "yalc@npm:1.0.0-pre.53"
dependencies:
chalk: ^4.1.0
detect-indent: ^6.0.0
fs-extra: ^8.0.1
glob: ^7.1.4
ignore: ^5.0.4
ini: ^2.0.0
npm-packlist: ^2.1.5
yargs: ^16.1.1
bin:
yalc: src/yalc.js
checksum: 3421e20039909fd0497e43ec05278e9f661d2300506deeaf06d8698e2f4dd37f905a267e85ec51e29811fd3d3844051172f4bb31049113774d502a69b0ecf2a1
languageName: node
linkType: hard
"yallist@npm:^3.0.2": "yallist@npm:^3.0.2":
version: 3.1.1 version: 3.1.1
resolution: "yallist@npm:3.1.1" resolution: "yallist@npm:3.1.1"
@ -29985,7 +30026,7 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"yargs@npm:^16.0.3, yargs@npm:^16.2.0": "yargs@npm:^16.0.3, yargs@npm:^16.1.1, yargs@npm:^16.2.0":
version: 16.2.0 version: 16.2.0
resolution: "yargs@npm:16.2.0" resolution: "yargs@npm:16.2.0"
dependencies: dependencies: