2024-05-07 10:06:35 +00:00
|
|
|
import react from '@vitejs/plugin-react-swc'
|
2023-05-05 13:10:36 +00:00
|
|
|
import path from 'path'
|
2023-12-27 17:17:18 +00:00
|
|
|
import { PluginOption, defineConfig } from 'vite'
|
2023-05-05 13:10:36 +00:00
|
|
|
|
2024-07-08 15:37:04 +00:00
|
|
|
const PR_NUMBER = process.env.VERCEL_GIT_PULL_REQUEST_ID
|
|
|
|
|
|
|
|
function getEnv() {
|
|
|
|
if (!process.env.VERCEL_ENV) {
|
|
|
|
return 'development'
|
|
|
|
}
|
|
|
|
if (PR_NUMBER !== undefined && PR_NUMBER !== '') {
|
|
|
|
return 'preview'
|
|
|
|
}
|
|
|
|
if (process.env.VERCEL_ENV === 'production') {
|
|
|
|
return 'production'
|
|
|
|
}
|
|
|
|
return 'canary'
|
|
|
|
}
|
|
|
|
|
|
|
|
const env = getEnv()
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
console.log('build env:', env)
|
|
|
|
|
|
|
|
function urlOrLocalFallback(mode: string, url: string | undefined, localFallbackPort: number) {
|
|
|
|
if (url) {
|
|
|
|
return JSON.stringify(url)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode === 'development') {
|
|
|
|
// in dev, vite lets us inline javascript expressions - so we return a template string that
|
|
|
|
// will be evaluated on the client
|
|
|
|
return '`http://${location.hostname}:' + localFallbackPort + '`'
|
|
|
|
} else {
|
|
|
|
// in production, we have to fall back to a hardcoded value
|
|
|
|
return JSON.stringify(`http://localhost:${localFallbackPort}`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const TLDRAW_BEMO_URL_STRING =
|
|
|
|
env === 'production'
|
2024-07-12 14:39:08 +00:00
|
|
|
? 'https://demo.tldraw.xyz'
|
2024-07-08 15:37:04 +00:00
|
|
|
: env === 'canary'
|
2024-07-12 14:39:08 +00:00
|
|
|
? 'https://canary-demo.tldraw.xyz'
|
2024-07-12 10:36:31 +00:00
|
|
|
: PR_NUMBER
|
2024-07-12 14:39:08 +00:00
|
|
|
? `https://pr-${PR_NUMBER}-demo.tldraw.xyz`
|
2024-07-12 10:36:31 +00:00
|
|
|
: undefined
|
2024-07-08 15:37:04 +00:00
|
|
|
|
|
|
|
export default defineConfig(({ mode }) => ({
|
2024-05-07 10:06:35 +00:00
|
|
|
plugins: [react({ tsDecorators: true }), exampleReadmePlugin()],
|
2023-05-05 13:10:36 +00:00
|
|
|
root: path.join(__dirname, 'src'),
|
|
|
|
publicDir: path.join(__dirname, 'public'),
|
|
|
|
build: {
|
|
|
|
outDir: path.join(__dirname, 'dist'),
|
|
|
|
assetsInlineLimit: 0,
|
2024-03-19 11:55:21 +00:00
|
|
|
target: 'es2022',
|
|
|
|
},
|
|
|
|
esbuild: {
|
|
|
|
target: 'es2022',
|
2023-05-05 13:10:36 +00:00
|
|
|
},
|
|
|
|
server: {
|
|
|
|
port: 5420,
|
|
|
|
},
|
|
|
|
clearScreen: false,
|
2023-05-09 17:31:19 +00:00
|
|
|
optimizeDeps: {
|
|
|
|
exclude: ['@tldraw/assets'],
|
2024-03-19 11:55:21 +00:00
|
|
|
esbuildOptions: {
|
|
|
|
target: 'es2022',
|
|
|
|
},
|
2023-05-09 17:31:19 +00:00
|
|
|
},
|
Feature flags rework (#1474)
This diff tweaks our `debugFlags` framework to support setting different
default value for different environments, makes it easier to define
feature flags, and makes feature flags show up in the debug menu by
default. With this change, feature flags will default to being enabled
in dev and preview environments, but disabled in production.
Specify a feature flag like this:
```ts
const featureFlags = {
myCoolNewFeature: createFeatureFlag('myCoolNewFeature')
}
```
optionally, pass a second value to control its defaults:
```ts
const featureFlags = {
featureEnabledInProduction: createFeatureFlag('someFeature', { all: true }),
customEnabled: createFeatureFlag('otherFeature', {development: true, staging: false, production: false}),
}
```
In code, the value can be read using `featureFlags.myFeature.value`.
Remember to wrap reading it in a reactive context!
### Change Type
- [x] `patch` — Bug Fix
### Test Plan
-
### Release Notes
[internal only change]
2023-05-30 13:06:15 +00:00
|
|
|
define: {
|
|
|
|
'process.env.TLDRAW_ENV': JSON.stringify(process.env.VERCEL_ENV ?? 'development'),
|
2024-07-12 10:36:31 +00:00
|
|
|
'process.env.TLDRAW_DEPLOY_ID': JSON.stringify(
|
|
|
|
process.env.VERCEL_GIT_COMMIT_SHA ?? `local-${Date.now()}`
|
|
|
|
),
|
2024-07-08 15:37:04 +00:00
|
|
|
'process.env.TLDRAW_BEMO_URL': urlOrLocalFallback(mode, TLDRAW_BEMO_URL_STRING, 8989),
|
2024-07-12 10:36:31 +00:00
|
|
|
'process.env.TLDRAW_IMAGE_URL': urlOrLocalFallback(
|
|
|
|
mode,
|
|
|
|
env === 'development' ? undefined : 'https://images.tldraw.xyz',
|
|
|
|
8989
|
|
|
|
),
|
Feature flags rework (#1474)
This diff tweaks our `debugFlags` framework to support setting different
default value for different environments, makes it easier to define
feature flags, and makes feature flags show up in the debug menu by
default. With this change, feature flags will default to being enabled
in dev and preview environments, but disabled in production.
Specify a feature flag like this:
```ts
const featureFlags = {
myCoolNewFeature: createFeatureFlag('myCoolNewFeature')
}
```
optionally, pass a second value to control its defaults:
```ts
const featureFlags = {
featureEnabledInProduction: createFeatureFlag('someFeature', { all: true }),
customEnabled: createFeatureFlag('otherFeature', {development: true, staging: false, production: false}),
}
```
In code, the value can be read using `featureFlags.myFeature.value`.
Remember to wrap reading it in a reactive context!
### Change Type
- [x] `patch` — Bug Fix
### Test Plan
-
### Release Notes
[internal only change]
2023-05-30 13:06:15 +00:00
|
|
|
},
|
2024-07-08 15:37:04 +00:00
|
|
|
}))
|
2023-12-27 17:17:18 +00:00
|
|
|
|
|
|
|
function exampleReadmePlugin(): PluginOption {
|
|
|
|
return {
|
|
|
|
name: 'example-readme',
|
|
|
|
async transform(src, id) {
|
|
|
|
const match = id.match(/examples\/src\/examples\/(.*)\/README.md$/)
|
|
|
|
if (!match) return
|
|
|
|
|
|
|
|
const remark = (await import('remark')).remark
|
|
|
|
const remarkFrontmatter = (await import('remark-frontmatter')).default
|
|
|
|
const remarkHtml = (await import('remark-html')).default
|
|
|
|
const matter = (await import('vfile-matter')).matter
|
|
|
|
|
|
|
|
const file = await remark()
|
|
|
|
.use(remarkFrontmatter)
|
|
|
|
.use(remarkHtml)
|
|
|
|
.use(() => (_, file) => matter(file))
|
|
|
|
.process(src)
|
|
|
|
|
|
|
|
const frontmatter = parseFrontMatter(file.data.matter, id)
|
|
|
|
|
|
|
|
const separator = '\n<hr>\n'
|
|
|
|
const parts = String(file).split(separator)
|
|
|
|
const description = parts[0]
|
|
|
|
const details = parts.slice(1).join(separator)
|
|
|
|
const path = `/${match[1]}`
|
|
|
|
const codeUrl = `https://github.com/tldraw/tldraw/tree/main/apps/examples/src/examples${path}`
|
|
|
|
|
|
|
|
const result = [
|
|
|
|
`export const title = ${JSON.stringify(frontmatter.title)};`,
|
2024-02-02 17:36:30 +00:00
|
|
|
`export const priority = ${JSON.stringify(frontmatter.priority ?? '100000')};`,
|
2024-01-29 10:04:41 +00:00
|
|
|
`export const category = ${JSON.stringify(frontmatter.category)};`,
|
2023-12-27 17:17:18 +00:00
|
|
|
`export const hide = ${JSON.stringify(frontmatter.hide)};`,
|
2024-07-12 10:36:31 +00:00
|
|
|
`export const multiplayer = ${JSON.stringify(frontmatter.multiplayer)};`,
|
2023-12-27 17:17:18 +00:00
|
|
|
`export const description = ${JSON.stringify(description)};`,
|
|
|
|
`export const details = ${JSON.stringify(details)};`,
|
|
|
|
`export const codeUrl = ${JSON.stringify(codeUrl)};`,
|
|
|
|
`export const path = ${JSON.stringify(path)};`,
|
|
|
|
`export const componentFile = ${JSON.stringify(frontmatter.component)};`,
|
|
|
|
`import {lazy} from 'react';`,
|
|
|
|
`export const loadComponent = async () => {`,
|
|
|
|
` return (await import(${JSON.stringify(frontmatter.component)})).default;`,
|
|
|
|
`};`,
|
2024-06-18 10:47:42 +00:00
|
|
|
`export const keywords = ${JSON.stringify(frontmatter.keywords)};`,
|
2023-12-27 17:17:18 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
return result.join('\n')
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function parseFrontMatter(data: unknown, fileName: string) {
|
|
|
|
if (!data || typeof data !== 'object') {
|
|
|
|
throw new Error(`Frontmatter missing in ${fileName}`)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!('title' in data && typeof data.title === 'string')) {
|
|
|
|
throw new Error(`Frontmatter key 'title' must be string in ${fileName}`)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!('component' in data && typeof data.component === 'string')) {
|
|
|
|
throw new Error(`Frontmatter key 'component' must be string in ${fileName}`)
|
|
|
|
}
|
|
|
|
|
2024-02-02 17:36:30 +00:00
|
|
|
const priority = 'priority' in data ? data.priority : 999999
|
2024-01-29 10:04:41 +00:00
|
|
|
if (typeof priority !== 'number') {
|
|
|
|
throw new Error(`Frontmatter key 'priority' must be number in ${fileName}`)
|
|
|
|
}
|
|
|
|
|
|
|
|
const category = 'category' in data ? data.category : null
|
|
|
|
if (typeof category !== 'string') {
|
|
|
|
throw new Error(`Frontmatter key 'category' must be string in ${fileName}`)
|
2023-12-27 17:17:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const hide = 'hide' in data ? data.hide : false
|
|
|
|
if (hide !== false && hide !== true) {
|
|
|
|
throw new Error(`Frontmatter key 'hide' must be boolean in ${fileName}`)
|
|
|
|
}
|
|
|
|
|
2024-06-18 10:47:42 +00:00
|
|
|
const keywords = 'keywords' in data ? data.keywords : []
|
|
|
|
if (!Array.isArray(keywords)) {
|
|
|
|
throw new Error(`Frontmatter key 'keywords' must be array in ${fileName}`)
|
|
|
|
}
|
|
|
|
|
2024-07-12 10:36:31 +00:00
|
|
|
const multiplayer = 'multiplayer' in data ? data.multiplayer : false
|
|
|
|
if (typeof multiplayer !== 'boolean') {
|
|
|
|
throw new Error(`Frontmatter key 'multiplayer' must be boolean in ${fileName}`)
|
|
|
|
}
|
|
|
|
|
2023-12-27 17:17:18 +00:00
|
|
|
return {
|
|
|
|
title: data.title,
|
|
|
|
component: data.component,
|
2024-01-29 10:04:41 +00:00
|
|
|
priority,
|
|
|
|
category,
|
2023-12-27 17:17:18 +00:00
|
|
|
hide,
|
2024-06-18 10:47:42 +00:00
|
|
|
keywords,
|
2024-07-12 10:36:31 +00:00
|
|
|
multiplayer,
|
2023-12-27 17:17:18 +00:00
|
|
|
}
|
|
|
|
}
|