2023-04-25 11:01:25 +00:00
|
|
|
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from 'fs'
|
|
|
|
import { join } from 'path'
|
sdk: wires up tldraw to have licensing mechanisms (#4021)
For non-commercial usage of tldraw, this adds a watermark in the corner,
both for branding purposes and as an incentive for our enterprise
customers to purchase a license.
For commercial usage of tldraw, you add a license to the `<Tldraw
licenseKey={YOUR_LICENSE_KEY} />` component so that the watermark
doesn't show.
The license is a signed key that has various bits of information in it,
such as:
- license type
- hosts that the license is valid for
- whether it's an internal-only license
- expiry date
We check the license on load and show a watermark (or throw an error if
internal-only) if the license is not valid in a production environment.
This is a @MitjaBezensek, @Taha-Hassan-Git, @mimecuvalo joint
production! 🤜 🤛
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [x] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. We will be dogfooding on staging.tldraw.com and tldraw.com itself
before releasing this.
### Release Notes
- SDK: wires up tldraw to have licensing mechanisms.
---------
Co-authored-by: Mitja Bezenšek <mitja.bezensek@gmail.com>
Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com>
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
2024-07-11 11:49:18 +00:00
|
|
|
import { SemVer } from 'semver'
|
2023-04-25 11:01:25 +00:00
|
|
|
import { optimize } from 'svgo'
|
sdk: wires up tldraw to have licensing mechanisms (#4021)
For non-commercial usage of tldraw, this adds a watermark in the corner,
both for branding purposes and as an incentive for our enterprise
customers to purchase a license.
For commercial usage of tldraw, you add a license to the `<Tldraw
licenseKey={YOUR_LICENSE_KEY} />` component so that the watermark
doesn't show.
The license is a signed key that has various bits of information in it,
such as:
- license type
- hosts that the license is valid for
- whether it's an internal-only license
- expiry date
We check the license on load and show a watermark (or throw an error if
internal-only) if the license is not valid in a production environment.
This is a @MitjaBezensek, @Taha-Hassan-Git, @mimecuvalo joint
production! 🤜 🤛
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [x] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. We will be dogfooding on staging.tldraw.com and tldraw.com itself
before releasing this.
### Release Notes
- SDK: wires up tldraw to have licensing mechanisms.
---------
Co-authored-by: Mitja Bezenšek <mitja.bezensek@gmail.com>
Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com>
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
2024-07-11 11:49:18 +00:00
|
|
|
import { publishDates, version } from './../packages/editor/src/version'
|
2023-04-25 11:01:25 +00:00
|
|
|
import {
|
|
|
|
readJsonIfExists,
|
2024-01-16 14:38:05 +00:00
|
|
|
REPO_ROOT,
|
2023-05-09 16:08:38 +00:00
|
|
|
writeCodeFile,
|
2023-04-25 11:01:25 +00:00
|
|
|
writeFile,
|
|
|
|
writeJsonFile,
|
|
|
|
writeStringFile,
|
|
|
|
} from './lib/file'
|
2023-06-01 18:01:49 +00:00
|
|
|
import { nicelog } from './lib/nicelog'
|
2023-04-25 11:01:25 +00:00
|
|
|
|
|
|
|
// We'll need to copy the assets into these folders
|
2024-01-16 14:38:05 +00:00
|
|
|
const PUBLIC_FOLDER_PATHS = [join(REPO_ROOT, 'packages', 'assets')]
|
2023-04-25 11:01:25 +00:00
|
|
|
|
|
|
|
const FONT_MAPPING: Record<string, string> = {
|
|
|
|
'IBMPlexMono-Medium': 'monospace',
|
|
|
|
'IBMPlexSerif-Medium': 'serif',
|
|
|
|
'IBMPlexSans-Medium': 'sansSerif',
|
2023-12-19 14:17:45 +00:00
|
|
|
'Shantell_Sans-Tldrawish': 'draw',
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2024-01-16 14:38:05 +00:00
|
|
|
const ASSETS_FOLDER_PATH = join(REPO_ROOT, 'assets')
|
2023-04-25 11:01:25 +00:00
|
|
|
|
2024-07-15 11:03:11 +00:00
|
|
|
const collectedAssetUrls: Record<
|
|
|
|
'fonts' | 'icons' | 'translations' | 'embedIcons',
|
|
|
|
Record<string, { file: string; hash?: string }>
|
|
|
|
> = {
|
2023-05-05 13:10:36 +00:00
|
|
|
fonts: {},
|
|
|
|
icons: {},
|
|
|
|
translations: {},
|
|
|
|
embedIcons: {},
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2024-07-15 11:03:11 +00:00
|
|
|
const mergedIconName = '0_merged.svg'
|
|
|
|
// this is how `svgo` starts each one of our optimized icon SVGs. if they don't all start with this,
|
|
|
|
// we can't merge them as it means they're of different size and are using different fill rules.
|
|
|
|
const mergedIconHeader =
|
|
|
|
'<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="none">'
|
|
|
|
const mergedIconFooter = '</svg>'
|
|
|
|
|
2023-04-25 11:01:25 +00:00
|
|
|
// 1. ICONS
|
|
|
|
|
|
|
|
async function copyIcons() {
|
|
|
|
// Get a list of all icons
|
|
|
|
const icons = readdirSync(join(ASSETS_FOLDER_PATH, 'icons', 'icon')).filter((icon) =>
|
|
|
|
icon.endsWith('.svg')
|
|
|
|
)
|
|
|
|
|
|
|
|
// Write list of names into icon-names.json (just the name, not extension)
|
|
|
|
const iconNames = icons.map((name) => name.replace('.svg', ''))
|
|
|
|
|
|
|
|
const sourceFolderPath = join(ASSETS_FOLDER_PATH, 'icons', 'icon')
|
|
|
|
|
|
|
|
// Create the optimized SVGs
|
|
|
|
const optimizedSvgs = icons.map((icon) => {
|
|
|
|
const iconPath = join(sourceFolderPath, icon)
|
|
|
|
const content = readFileSync(iconPath, 'utf8')
|
|
|
|
const svg = optimize(content, { path: iconPath })
|
|
|
|
return { fileName: icon, data: svg.data }
|
|
|
|
})
|
|
|
|
|
2024-07-15 11:03:11 +00:00
|
|
|
// Merge svgs into a single file
|
|
|
|
const mergedSvgParts = [
|
|
|
|
mergedIconHeader,
|
|
|
|
'<style>',
|
|
|
|
// in order to target individual icons with the URL hash, we need to hide all of them by
|
|
|
|
// default...
|
|
|
|
'svg > [id] { display: none; }',
|
|
|
|
// ...then show the one that's targeted
|
|
|
|
'svg > [id]:target { display: inline; }',
|
|
|
|
'</style>',
|
|
|
|
]
|
|
|
|
for (const { fileName, data } of optimizedSvgs) {
|
|
|
|
if (!data.startsWith(mergedIconHeader) || !data.endsWith(mergedIconFooter)) {
|
|
|
|
nicelog('SVG does not match expected format for merging:', fileName)
|
|
|
|
process.exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
const id = fileName.replace('.svg', '')
|
|
|
|
const svgContent = data.slice(mergedIconHeader.length, -mergedIconFooter.length)
|
|
|
|
mergedSvgParts.push(`<g id="${id}">${svgContent}</g>`)
|
|
|
|
}
|
|
|
|
mergedSvgParts.push(mergedIconFooter)
|
|
|
|
const mergedSvg = optimize(mergedSvgParts.join('\n')).data
|
|
|
|
optimizedSvgs.push({ fileName: mergedIconName, data: mergedSvg })
|
|
|
|
|
2023-04-25 11:01:25 +00:00
|
|
|
// Optimize all of the svg icons and write them into the new folders
|
|
|
|
for (const folderPath of PUBLIC_FOLDER_PATHS) {
|
|
|
|
const publicIconsRootFolderPath = join(folderPath, 'icons')
|
|
|
|
const pulicIconsFolderPath = join(publicIconsRootFolderPath, 'icon')
|
|
|
|
|
|
|
|
if (existsSync(publicIconsRootFolderPath)) {
|
|
|
|
rmSync(publicIconsRootFolderPath, { recursive: true })
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the folders
|
|
|
|
mkdirSync(publicIconsRootFolderPath, { recursive: true })
|
|
|
|
mkdirSync(pulicIconsFolderPath, { recursive: true })
|
|
|
|
|
|
|
|
// Copy each optimized icons into the new folder
|
|
|
|
for (const { fileName, data } of optimizedSvgs) {
|
|
|
|
await writeStringFile(join(pulicIconsFolderPath, fileName), data)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the JSON file containing all of the names of the icons
|
|
|
|
await writeJsonFile(join(pulicIconsFolderPath, 'icon-names.json'), iconNames)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the names of all of the svg icons and create a TypeScript file of valid icon names
|
|
|
|
const iconTypeFile = `
|
|
|
|
/** @public */
|
|
|
|
export type TLUiIconType =
|
|
|
|
${icons.map((icon) => JSON.stringify(icon.replace('.svg', ''))).join(' | ')}
|
|
|
|
|
|
|
|
/** @public */
|
2023-06-02 21:16:09 +00:00
|
|
|
export const iconTypes = [
|
2023-04-25 11:01:25 +00:00
|
|
|
${icons.map((icon) => JSON.stringify(icon.replace('.svg', ''))).join(', ')}
|
|
|
|
] as const`
|
|
|
|
|
2023-05-09 16:08:38 +00:00
|
|
|
await writeCodeFile(
|
|
|
|
'scripts/refresh-assets.ts',
|
|
|
|
'typescript',
|
2024-01-16 14:38:05 +00:00
|
|
|
join(REPO_ROOT, 'packages', 'tldraw', 'src', 'lib', 'ui', 'icon-types.ts'),
|
2023-04-25 11:01:25 +00:00
|
|
|
iconTypeFile
|
|
|
|
)
|
|
|
|
|
|
|
|
// add to the asset declaration file
|
|
|
|
for (const icon of icons) {
|
|
|
|
const name = icon.replace('.svg', '')
|
2024-07-15 11:03:11 +00:00
|
|
|
collectedAssetUrls.icons[name] = {
|
|
|
|
file: `icons/icon/${mergedIconName}`,
|
|
|
|
hash: name,
|
|
|
|
}
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. EMBED-ICONS
|
|
|
|
|
|
|
|
async function copyEmbedIcons() {
|
|
|
|
const folderName = 'embed-icons'
|
|
|
|
const extension = '.png'
|
|
|
|
|
|
|
|
const sourceFolderPath = join(ASSETS_FOLDER_PATH, folderName)
|
|
|
|
const itemsToCopy = readdirSync(sourceFolderPath).filter((icon) => icon.endsWith(extension))
|
|
|
|
|
|
|
|
for (const publicFolderPath of PUBLIC_FOLDER_PATHS) {
|
|
|
|
const destinationFolderPath = join(publicFolderPath, folderName)
|
|
|
|
|
|
|
|
// Delete the folder if it exists
|
|
|
|
if (existsSync(destinationFolderPath)) {
|
|
|
|
rmSync(destinationFolderPath, { recursive: true })
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the new folder
|
|
|
|
mkdirSync(destinationFolderPath, { recursive: true })
|
|
|
|
|
|
|
|
// Copy all items into the new folder
|
|
|
|
for (const item of itemsToCopy) {
|
|
|
|
await writeFile(join(destinationFolderPath, item), readFileSync(join(sourceFolderPath, item)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add to the asset declaration file
|
|
|
|
for (const item of itemsToCopy) {
|
|
|
|
const name = item.replace(extension, '')
|
2024-07-15 11:03:11 +00:00
|
|
|
collectedAssetUrls.embedIcons[name] = { file: `${folderName}/${item}` }
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. FONTS
|
|
|
|
|
|
|
|
async function copyFonts() {
|
|
|
|
const folderName = 'fonts'
|
|
|
|
const extension = '.woff2'
|
|
|
|
|
|
|
|
const sourceFolderPath = join(ASSETS_FOLDER_PATH, folderName)
|
|
|
|
const itemsToCopy = readdirSync(sourceFolderPath).filter((icon) => icon.endsWith(extension))
|
|
|
|
|
|
|
|
for (const publicFolderPath of PUBLIC_FOLDER_PATHS) {
|
|
|
|
const destinationFolderPath = join(publicFolderPath, folderName)
|
|
|
|
|
|
|
|
// Delete the folder if it exists
|
|
|
|
if (existsSync(destinationFolderPath)) {
|
|
|
|
rmSync(destinationFolderPath, { recursive: true })
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the new folder
|
|
|
|
mkdirSync(destinationFolderPath)
|
|
|
|
|
|
|
|
// Copy all items into the new folder
|
|
|
|
for (const item of itemsToCopy) {
|
|
|
|
await writeFile(join(destinationFolderPath, item), readFileSync(join(sourceFolderPath, item)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add to the asset declaration file
|
|
|
|
for (const item of itemsToCopy) {
|
|
|
|
const itemWithoutExtension = item.replace(extension, '')
|
|
|
|
const name = FONT_MAPPING[itemWithoutExtension]
|
|
|
|
if (!name) {
|
2023-06-01 18:01:49 +00:00
|
|
|
nicelog('Font mapping not found for', itemWithoutExtension)
|
2023-04-25 11:01:25 +00:00
|
|
|
process.exit(1)
|
|
|
|
}
|
2024-07-15 11:03:11 +00:00
|
|
|
collectedAssetUrls.fonts[name] = { file: `${folderName}/${item}` }
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. TRANSLATIONS
|
|
|
|
|
|
|
|
async function copyTranslations() {
|
|
|
|
const folderName = 'translations'
|
|
|
|
const extension = '.json'
|
|
|
|
|
|
|
|
const sourceFolderPath = join(ASSETS_FOLDER_PATH, folderName)
|
|
|
|
const itemsToCopy = readdirSync(sourceFolderPath).filter((item) => item.endsWith(extension))
|
|
|
|
|
|
|
|
for (const publicFolderPath of PUBLIC_FOLDER_PATHS) {
|
|
|
|
const destinationFolderPath = join(publicFolderPath, folderName)
|
|
|
|
|
|
|
|
// Delete the folder if it exists
|
|
|
|
if (existsSync(destinationFolderPath)) {
|
|
|
|
rmSync(destinationFolderPath, { recursive: true })
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the new folder
|
|
|
|
mkdirSync(destinationFolderPath)
|
|
|
|
|
|
|
|
// Copy all items into the new folder
|
|
|
|
for (const item of itemsToCopy) {
|
|
|
|
await writeFile(join(destinationFolderPath, item), readFileSync(join(sourceFolderPath, item)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create hardcoded files
|
tldraw zero - package shuffle (#1710)
This PR moves code between our packages so that:
- @tldraw/editor is a “core” library with the engine and canvas but no
shapes, tools, or other things
- @tldraw/tldraw contains everything particular to the experience we’ve
built for tldraw
At first look, this might seem like a step away from customization and
configuration, however I believe it greatly increases the configuration
potential of the @tldraw/editor while also providing a more accurate
reflection of what configuration options actually exist for
@tldraw/tldraw.
## Library changes
@tldraw/editor re-exports its dependencies and @tldraw/tldraw re-exports
@tldraw/editor.
- users of @tldraw/editor WITHOUT @tldraw/tldraw should almost always
only import things from @tldraw/editor.
- users of @tldraw/tldraw should almost always only import things from
@tldraw/tldraw.
- @tldraw/polyfills is merged into @tldraw/editor
- @tldraw/indices is merged into @tldraw/editor
- @tldraw/primitives is merged mostly into @tldraw/editor, partially
into @tldraw/tldraw
- @tldraw/file-format is merged into @tldraw/tldraw
- @tldraw/ui is merged into @tldraw/tldraw
Many (many) utils and other code is moved from the editor to tldraw. For
example, embeds now are entirely an feature of @tldraw/tldraw. The only
big chunk of code left in core is related to arrow handling.
## API Changes
The editor can now be used without tldraw's assets. We load them in
@tldraw/tldraw instead, so feel free to use whatever fonts or images or
whatever that you like with the editor.
All tools and shapes (except for the `Group` shape) are moved to
@tldraw/tldraw. This includes the `select` tool.
You should use the editor with at least one tool, however, so you now
also need to send in an `initialState` prop to the Editor /
<TldrawEditor> component indicating which state the editor should begin
in.
The `components` prop now also accepts `SelectionForeground`.
The complex selection component that we use for tldraw is moved to
@tldraw/tldraw. The default component is quite basic but can easily be
replaced via the `components` prop. We pass down our tldraw-flavored
SelectionFg via `components`.
Likewise with the `Scribble` component: the `DefaultScribble` no longer
uses our freehand tech and is a simple path instead. We pass down the
tldraw-flavored scribble via `components`.
The `ExternalContentManager` (`Editor.externalContentManager`) is
removed and replaced with a mapping of types to handlers.
- Register new content handlers with
`Editor.registerExternalContentHandler`.
- Register new asset creation handlers (for files and URLs) with
`Editor.registerExternalAssetHandler`
### Change Type
- [x] `major` — Breaking change
### Test Plan
- [x] Unit Tests
- [x] End to end tests
### Release Notes
- [@tldraw/editor] lots, wip
- [@tldraw/ui] gone, merged to tldraw/tldraw
- [@tldraw/polyfills] gone, merged to tldraw/editor
- [@tldraw/primitives] gone, merged to tldraw/editor / tldraw/tldraw
- [@tldraw/indices] gone, merged to tldraw/editor
- [@tldraw/file-format] gone, merged to tldraw/tldraw
---------
Co-authored-by: alex <alex@dytry.ch>
2023-07-17 21:22:34 +00:00
|
|
|
const uiPath = join(
|
2024-01-16 14:38:05 +00:00
|
|
|
REPO_ROOT,
|
tldraw zero - package shuffle (#1710)
This PR moves code between our packages so that:
- @tldraw/editor is a “core” library with the engine and canvas but no
shapes, tools, or other things
- @tldraw/tldraw contains everything particular to the experience we’ve
built for tldraw
At first look, this might seem like a step away from customization and
configuration, however I believe it greatly increases the configuration
potential of the @tldraw/editor while also providing a more accurate
reflection of what configuration options actually exist for
@tldraw/tldraw.
## Library changes
@tldraw/editor re-exports its dependencies and @tldraw/tldraw re-exports
@tldraw/editor.
- users of @tldraw/editor WITHOUT @tldraw/tldraw should almost always
only import things from @tldraw/editor.
- users of @tldraw/tldraw should almost always only import things from
@tldraw/tldraw.
- @tldraw/polyfills is merged into @tldraw/editor
- @tldraw/indices is merged into @tldraw/editor
- @tldraw/primitives is merged mostly into @tldraw/editor, partially
into @tldraw/tldraw
- @tldraw/file-format is merged into @tldraw/tldraw
- @tldraw/ui is merged into @tldraw/tldraw
Many (many) utils and other code is moved from the editor to tldraw. For
example, embeds now are entirely an feature of @tldraw/tldraw. The only
big chunk of code left in core is related to arrow handling.
## API Changes
The editor can now be used without tldraw's assets. We load them in
@tldraw/tldraw instead, so feel free to use whatever fonts or images or
whatever that you like with the editor.
All tools and shapes (except for the `Group` shape) are moved to
@tldraw/tldraw. This includes the `select` tool.
You should use the editor with at least one tool, however, so you now
also need to send in an `initialState` prop to the Editor /
<TldrawEditor> component indicating which state the editor should begin
in.
The `components` prop now also accepts `SelectionForeground`.
The complex selection component that we use for tldraw is moved to
@tldraw/tldraw. The default component is quite basic but can easily be
replaced via the `components` prop. We pass down our tldraw-flavored
SelectionFg via `components`.
Likewise with the `Scribble` component: the `DefaultScribble` no longer
uses our freehand tech and is a simple path instead. We pass down the
tldraw-flavored scribble via `components`.
The `ExternalContentManager` (`Editor.externalContentManager`) is
removed and replaced with a mapping of types to handlers.
- Register new content handlers with
`Editor.registerExternalContentHandler`.
- Register new asset creation handlers (for files and URLs) with
`Editor.registerExternalAssetHandler`
### Change Type
- [x] `major` — Breaking change
### Test Plan
- [x] Unit Tests
- [x] End to end tests
### Release Notes
- [@tldraw/editor] lots, wip
- [@tldraw/ui] gone, merged to tldraw/tldraw
- [@tldraw/polyfills] gone, merged to tldraw/editor
- [@tldraw/primitives] gone, merged to tldraw/editor / tldraw/tldraw
- [@tldraw/indices] gone, merged to tldraw/editor
- [@tldraw/file-format] gone, merged to tldraw/tldraw
---------
Co-authored-by: alex <alex@dytry.ch>
2023-07-17 21:22:34 +00:00
|
|
|
'packages',
|
|
|
|
'tldraw',
|
|
|
|
'src',
|
|
|
|
'lib',
|
|
|
|
'ui',
|
|
|
|
'hooks',
|
|
|
|
'useTranslation'
|
|
|
|
)
|
2023-04-25 11:01:25 +00:00
|
|
|
|
|
|
|
// languages.ts
|
2023-06-03 20:46:53 +00:00
|
|
|
|
2023-04-25 11:01:25 +00:00
|
|
|
const languagesSource = await readJsonIfExists(join(sourceFolderPath, 'languages.json'))!
|
2024-05-22 15:55:49 +00:00
|
|
|
interface Language {
|
|
|
|
label: string
|
|
|
|
locale: string
|
|
|
|
}
|
2023-04-25 11:01:25 +00:00
|
|
|
const languagesFile = `
|
|
|
|
/** @public */
|
2024-01-25 11:26:33 +00:00
|
|
|
export const LANGUAGES = ${JSON.stringify(
|
2024-02-19 11:14:33 +00:00
|
|
|
languagesSource.sort((a: Language, b: Language) => a.label.localeCompare(b.label, 'en'))
|
2024-01-25 11:26:33 +00:00
|
|
|
)} as const
|
2023-04-25 11:01:25 +00:00
|
|
|
`
|
2024-01-16 14:38:05 +00:00
|
|
|
const schemaPath = join(REPO_ROOT, 'packages', 'tlschema', 'src', 'translations')
|
2023-04-25 11:01:25 +00:00
|
|
|
const schemaLanguagesFilePath = join(schemaPath, 'languages.ts')
|
2023-05-09 16:08:38 +00:00
|
|
|
await writeCodeFile(
|
|
|
|
'scripts/refresh-assets.ts',
|
|
|
|
'typescript',
|
|
|
|
schemaLanguagesFilePath,
|
|
|
|
languagesFile
|
|
|
|
)
|
2023-04-25 11:01:25 +00:00
|
|
|
|
|
|
|
// main.ts
|
|
|
|
|
|
|
|
const defaultTranslation = await readJsonIfExists(join(sourceFolderPath, 'main.json'))!
|
|
|
|
const defaultTranslationFilePath = join(uiPath, 'defaultTranslation.ts')
|
|
|
|
const defaultTranslationFile = `
|
|
|
|
/** @internal */
|
|
|
|
export const DEFAULT_TRANSLATION = ${JSON.stringify(defaultTranslation)}
|
|
|
|
`
|
2023-05-09 16:08:38 +00:00
|
|
|
await writeCodeFile(
|
|
|
|
'scripts/refresh-assets.ts',
|
|
|
|
'typescript',
|
|
|
|
defaultTranslationFilePath,
|
|
|
|
defaultTranslationFile
|
|
|
|
)
|
2023-04-25 11:01:25 +00:00
|
|
|
|
|
|
|
// translationKeys.ts
|
|
|
|
|
|
|
|
const translationKeys = Object.keys(defaultTranslation).map((key) => `'${key}'`)
|
2023-06-02 21:16:09 +00:00
|
|
|
const translationKeysFilePath = join(uiPath, 'TLUiTranslationKey.ts')
|
2023-04-25 11:01:25 +00:00
|
|
|
const translationKeysFile = `
|
|
|
|
/** @public */
|
2023-06-02 21:16:09 +00:00
|
|
|
export type TLUiTranslationKey = ${translationKeys.join(' | ')}
|
2023-04-25 11:01:25 +00:00
|
|
|
`
|
2023-05-09 16:08:38 +00:00
|
|
|
await writeCodeFile(
|
|
|
|
'scripts/refresh-assets.ts',
|
|
|
|
'typescript',
|
|
|
|
translationKeysFilePath,
|
|
|
|
translationKeysFile
|
|
|
|
)
|
2023-04-25 11:01:25 +00:00
|
|
|
|
|
|
|
// add to the asset declaration file
|
|
|
|
for (const item of itemsToCopy) {
|
|
|
|
const name = item.replace(extension, '')
|
2024-07-15 11:03:11 +00:00
|
|
|
collectedAssetUrls.translations[name] = { file: `${folderName}/${item}` }
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
sdk: wires up tldraw to have licensing mechanisms (#4021)
For non-commercial usage of tldraw, this adds a watermark in the corner,
both for branding purposes and as an incentive for our enterprise
customers to purchase a license.
For commercial usage of tldraw, you add a license to the `<Tldraw
licenseKey={YOUR_LICENSE_KEY} />` component so that the watermark
doesn't show.
The license is a signed key that has various bits of information in it,
such as:
- license type
- hosts that the license is valid for
- whether it's an internal-only license
- expiry date
We check the license on load and show a watermark (or throw an error if
internal-only) if the license is not valid in a production environment.
This is a @MitjaBezensek, @Taha-Hassan-Git, @mimecuvalo joint
production! 🤜 🤛
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [x] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. We will be dogfooding on staging.tldraw.com and tldraw.com itself
before releasing this.
### Release Notes
- SDK: wires up tldraw to have licensing mechanisms.
---------
Co-authored-by: Mitja Bezenšek <mitja.bezensek@gmail.com>
Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com>
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
2024-07-11 11:49:18 +00:00
|
|
|
// 4. WATERMARKS
|
|
|
|
async function copyWatermarks() {
|
|
|
|
const folderName = 'watermarks'
|
|
|
|
const extension = '.svg'
|
|
|
|
|
|
|
|
const sourceFolderPath = join(ASSETS_FOLDER_PATH, folderName)
|
|
|
|
const itemsToCopy = readdirSync(sourceFolderPath).filter((watermark) =>
|
|
|
|
watermark.endsWith(extension)
|
|
|
|
)
|
|
|
|
|
2024-07-16 10:41:55 +00:00
|
|
|
const optimizedItems = itemsToCopy.map((watermark) => {
|
|
|
|
const watermarkPath = join(sourceFolderPath, watermark)
|
|
|
|
const content = readFileSync(watermarkPath, 'utf8')
|
|
|
|
const svg = optimize(content, { path: watermarkPath })
|
|
|
|
return { fileName: watermark, data: svg.data }
|
|
|
|
})
|
|
|
|
|
|
|
|
const file = new CodeFile()
|
|
|
|
for (const { fileName, data } of optimizedItems) {
|
|
|
|
const varName = file.formatName(fileName)
|
|
|
|
file.append(`export const ${varName} = ${JSON.stringify(data)};`)
|
sdk: wires up tldraw to have licensing mechanisms (#4021)
For non-commercial usage of tldraw, this adds a watermark in the corner,
both for branding purposes and as an incentive for our enterprise
customers to purchase a license.
For commercial usage of tldraw, you add a license to the `<Tldraw
licenseKey={YOUR_LICENSE_KEY} />` component so that the watermark
doesn't show.
The license is a signed key that has various bits of information in it,
such as:
- license type
- hosts that the license is valid for
- whether it's an internal-only license
- expiry date
We check the license on load and show a watermark (or throw an error if
internal-only) if the license is not valid in a production environment.
This is a @MitjaBezensek, @Taha-Hassan-Git, @mimecuvalo joint
production! 🤜 🤛
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [x] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. We will be dogfooding on staging.tldraw.com and tldraw.com itself
before releasing this.
### Release Notes
- SDK: wires up tldraw to have licensing mechanisms.
---------
Co-authored-by: Mitja Bezenšek <mitja.bezensek@gmail.com>
Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com>
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
2024-07-11 11:49:18 +00:00
|
|
|
}
|
2024-07-16 10:41:55 +00:00
|
|
|
|
|
|
|
const destinationFolderPath = join(
|
|
|
|
REPO_ROOT,
|
|
|
|
'packages',
|
|
|
|
'editor',
|
|
|
|
'src',
|
|
|
|
'lib',
|
|
|
|
'editor',
|
|
|
|
'managers',
|
|
|
|
'watermarks.ts'
|
|
|
|
)
|
|
|
|
await writeCodeFile(
|
|
|
|
'scripts/refresh-assets.ts',
|
|
|
|
'typescript',
|
|
|
|
destinationFolderPath,
|
|
|
|
file.toString()
|
|
|
|
)
|
sdk: wires up tldraw to have licensing mechanisms (#4021)
For non-commercial usage of tldraw, this adds a watermark in the corner,
both for branding purposes and as an incentive for our enterprise
customers to purchase a license.
For commercial usage of tldraw, you add a license to the `<Tldraw
licenseKey={YOUR_LICENSE_KEY} />` component so that the watermark
doesn't show.
The license is a signed key that has various bits of information in it,
such as:
- license type
- hosts that the license is valid for
- whether it's an internal-only license
- expiry date
We check the license on load and show a watermark (or throw an error if
internal-only) if the license is not valid in a production environment.
This is a @MitjaBezensek, @Taha-Hassan-Git, @mimecuvalo joint
production! 🤜 🤛
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [x] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. We will be dogfooding on staging.tldraw.com and tldraw.com itself
before releasing this.
### Release Notes
- SDK: wires up tldraw to have licensing mechanisms.
---------
Co-authored-by: Mitja Bezenšek <mitja.bezensek@gmail.com>
Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com>
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
2024-07-11 11:49:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 5. ASSET DECLARATION FILES
|
2023-05-05 13:10:36 +00:00
|
|
|
async function writeUrlBasedAssetDeclarationFile() {
|
2024-01-16 14:38:05 +00:00
|
|
|
const codeFilePath = join(REPO_ROOT, 'packages', 'assets', 'urls.js')
|
2024-07-15 11:03:11 +00:00
|
|
|
const codeFile = new CodeFile(`
|
2023-04-25 11:01:25 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
2023-05-09 16:08:38 +00:00
|
|
|
/// <reference path="./modules.d.ts" />
|
2023-06-09 11:43:01 +00:00
|
|
|
import { formatAssetUrl } from './utils.js'
|
2024-07-15 11:03:11 +00:00
|
|
|
`)
|
2023-05-09 16:08:38 +00:00
|
|
|
|
2024-07-15 11:03:11 +00:00
|
|
|
const fn = codeFile.appendFn(`
|
2023-05-09 16:08:38 +00:00
|
|
|
/**
|
|
|
|
* @param {AssetUrlOptions} [opts]
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function getAssetUrlsByMetaUrl(opts) {
|
2024-07-15 11:03:11 +00:00
|
|
|
`)
|
|
|
|
|
|
|
|
fn.append('return {')
|
|
|
|
for (const [type, assets] of Object.entries(collectedAssetUrls)) {
|
|
|
|
fn.append(`${type}: {`)
|
|
|
|
for (const [name, { file, hash }] of Object.entries(assets)) {
|
|
|
|
const href = JSON.stringify(`./${file}`)
|
|
|
|
const fileUrl = fn.memo(file, `formatAssetUrl(new URL(${href}, import.meta.url).href, opts)`)
|
|
|
|
const value = hash ? `${fileUrl} + ${JSON.stringify('#' + hash)}` : fileUrl
|
|
|
|
fn.append(`${JSON.stringify(name)}: ${value},`)
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
2024-07-15 11:03:11 +00:00
|
|
|
fn.append('},')
|
|
|
|
}
|
|
|
|
fn.append('}')
|
2023-04-25 11:01:25 +00:00
|
|
|
|
2024-07-15 11:03:11 +00:00
|
|
|
await writeCodeFile('scripts/refresh-assets.ts', 'javascript', codeFilePath, codeFile.toString())
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
2023-06-09 11:43:01 +00:00
|
|
|
|
2024-01-19 15:31:01 +00:00
|
|
|
async function writeImportBasedAssetDeclarationFile(
|
|
|
|
importSuffix: string,
|
|
|
|
fileName: string
|
|
|
|
): Promise<void> {
|
2024-07-15 11:03:11 +00:00
|
|
|
const codeFile = new CodeFile(`
|
2023-05-05 13:10:36 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
2023-05-09 16:08:38 +00:00
|
|
|
/// <reference path="./modules.d.ts" />
|
2023-06-09 11:43:01 +00:00
|
|
|
import { formatAssetUrl } from './utils.js'
|
2024-07-15 11:03:11 +00:00
|
|
|
`)
|
2023-06-09 11:43:01 +00:00
|
|
|
|
2024-07-15 11:03:11 +00:00
|
|
|
const fn = codeFile.appendFn(`
|
2023-05-09 16:08:38 +00:00
|
|
|
/**
|
|
|
|
* @param {AssetUrlOptions} [opts]
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function getAssetUrlsByImport(opts) {
|
2024-07-15 11:03:11 +00:00
|
|
|
`)
|
2023-05-05 13:10:36 +00:00
|
|
|
|
2024-07-15 11:03:11 +00:00
|
|
|
fn.append('return {')
|
2023-05-05 13:10:36 +00:00
|
|
|
for (const [type, assets] of Object.entries(collectedAssetUrls)) {
|
2024-07-15 11:03:11 +00:00
|
|
|
fn.append(`${type}: {`)
|
|
|
|
for (const [name, { file, hash }] of Object.entries(assets)) {
|
|
|
|
const variableName = codeFile.import(`./${file}${importSuffix}`)
|
|
|
|
const formattedUrl = fn.memo(file, `formatAssetUrl(${variableName}, opts)`)
|
|
|
|
const value = hash ? `${formattedUrl} + ${JSON.stringify('#' + hash)}` : formattedUrl
|
|
|
|
fn.append(`${JSON.stringify(name)}: ${value},`)
|
2023-05-05 13:10:36 +00:00
|
|
|
}
|
2024-07-15 11:03:11 +00:00
|
|
|
fn.append('},')
|
2023-05-05 13:10:36 +00:00
|
|
|
}
|
2024-07-15 11:03:11 +00:00
|
|
|
fn.append('}')
|
2023-05-09 16:08:38 +00:00
|
|
|
|
2024-01-19 15:31:01 +00:00
|
|
|
const codeFilePath = join(REPO_ROOT, 'packages', 'assets', fileName)
|
2024-07-15 11:03:11 +00:00
|
|
|
await writeCodeFile('scripts/refresh-assets.ts', 'javascript', codeFilePath, codeFile.toString())
|
2023-06-09 11:43:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async function writeSelfHostedAssetDeclarationFile(): Promise<void> {
|
2024-01-16 14:38:05 +00:00
|
|
|
const codeFilePath = join(REPO_ROOT, 'packages', 'assets', 'selfHosted.js')
|
2024-07-15 11:03:11 +00:00
|
|
|
const codeFile = new CodeFile(`
|
2023-06-09 11:43:01 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
|
|
|
/// <reference path="./modules.d.ts" />
|
|
|
|
import { formatAssetUrl } from './utils.js'
|
2024-07-15 11:03:11 +00:00
|
|
|
`)
|
2023-05-09 16:08:38 +00:00
|
|
|
|
2024-07-15 11:03:11 +00:00
|
|
|
const fn = codeFile.appendFn(`
|
2023-06-09 11:43:01 +00:00
|
|
|
/**
|
|
|
|
* @param {AssetUrlOptions} [opts]
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function getAssetUrls(opts) {
|
2024-07-15 11:03:11 +00:00
|
|
|
`)
|
|
|
|
|
|
|
|
fn.append('return {')
|
|
|
|
for (const [type, assets] of Object.entries(collectedAssetUrls)) {
|
|
|
|
fn.append(`${type}: {`)
|
|
|
|
for (const [name, { file, hash }] of Object.entries(assets)) {
|
|
|
|
const href = JSON.stringify(`./${file}`)
|
|
|
|
const formattedUrl = fn.memo(file, `formatAssetUrl(${href}, opts)`)
|
|
|
|
const value = hash ? `${formattedUrl} + ${JSON.stringify('#' + hash)}` : formattedUrl
|
|
|
|
fn.append(`${JSON.stringify(name)}: ${value},`)
|
2023-06-09 11:43:01 +00:00
|
|
|
}
|
2024-07-15 11:03:11 +00:00
|
|
|
fn.append('},')
|
|
|
|
}
|
|
|
|
fn.append('}')
|
2023-06-09 11:43:01 +00:00
|
|
|
|
2024-07-15 11:03:11 +00:00
|
|
|
await writeCodeFile('scripts/refresh-assets.ts', 'javascript', codeFilePath, codeFile.toString())
|
2023-05-09 16:08:38 +00:00
|
|
|
}
|
|
|
|
|
2023-06-09 11:43:01 +00:00
|
|
|
async function writeAssetDeclarationDTSFile() {
|
2023-05-09 16:08:38 +00:00
|
|
|
let dts = `
|
2023-06-09 11:43:01 +00:00
|
|
|
export type AssetUrl = string | { src: string }
|
|
|
|
export type AssetUrlOptions = { baseUrl?: string } | ((assetUrl: string) => string)
|
|
|
|
export type AssetUrls = {
|
2023-05-09 16:08:38 +00:00
|
|
|
`
|
|
|
|
|
|
|
|
for (const [type, assets] of Object.entries(collectedAssetUrls)) {
|
|
|
|
dts += `${type}: {\n`
|
|
|
|
for (const name of Object.keys(assets)) {
|
|
|
|
dts += `${JSON.stringify(name)}: string,\n`
|
|
|
|
}
|
|
|
|
dts += '},\n'
|
|
|
|
}
|
|
|
|
|
|
|
|
dts += `
|
2023-05-05 13:10:36 +00:00
|
|
|
}
|
|
|
|
`
|
2023-04-25 11:01:25 +00:00
|
|
|
|
2024-01-16 14:38:05 +00:00
|
|
|
const assetDeclarationFilePath = join(REPO_ROOT, 'packages', 'assets', 'types.d.ts')
|
2023-05-09 16:08:38 +00:00
|
|
|
await writeCodeFile('scripts/refresh-assets.ts', 'typescript', assetDeclarationFilePath, dts)
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
sdk: wires up tldraw to have licensing mechanisms (#4021)
For non-commercial usage of tldraw, this adds a watermark in the corner,
both for branding purposes and as an incentive for our enterprise
customers to purchase a license.
For commercial usage of tldraw, you add a license to the `<Tldraw
licenseKey={YOUR_LICENSE_KEY} />` component so that the watermark
doesn't show.
The license is a signed key that has various bits of information in it,
such as:
- license type
- hosts that the license is valid for
- whether it's an internal-only license
- expiry date
We check the license on load and show a watermark (or throw an error if
internal-only) if the license is not valid in a production environment.
This is a @MitjaBezensek, @Taha-Hassan-Git, @mimecuvalo joint
production! 🤜 🤛
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [x] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. We will be dogfooding on staging.tldraw.com and tldraw.com itself
before releasing this.
### Release Notes
- SDK: wires up tldraw to have licensing mechanisms.
---------
Co-authored-by: Mitja Bezenšek <mitja.bezensek@gmail.com>
Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com>
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
2024-07-11 11:49:18 +00:00
|
|
|
function getNewPublishDates(packageVersion: string) {
|
|
|
|
const currentVersion = new SemVer(version)
|
|
|
|
const currentPackageVersion = new SemVer(packageVersion)
|
|
|
|
const now = new Date().toISOString()
|
|
|
|
if (currentPackageVersion.major > currentVersion.major) {
|
|
|
|
return {
|
|
|
|
major: now,
|
|
|
|
minor: now,
|
|
|
|
patch: now,
|
|
|
|
}
|
|
|
|
} else if (currentPackageVersion.minor > currentVersion.minor) {
|
|
|
|
return {
|
|
|
|
major: publishDates.major,
|
|
|
|
minor: now,
|
|
|
|
patch: now,
|
|
|
|
}
|
|
|
|
} else if (currentPackageVersion.patch > currentVersion.patch) {
|
|
|
|
return {
|
|
|
|
major: publishDates.major,
|
|
|
|
minor: publishDates.minor,
|
|
|
|
patch: now,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return publishDates
|
|
|
|
}
|
|
|
|
|
2024-02-26 18:30:55 +00:00
|
|
|
async function copyVersionToDotCom() {
|
2024-03-11 17:52:58 +00:00
|
|
|
const packageJson = await readJsonIfExists(join(REPO_ROOT, 'packages', 'tldraw', 'package.json'))
|
|
|
|
const packageVersion = packageJson.version
|
sdk: wires up tldraw to have licensing mechanisms (#4021)
For non-commercial usage of tldraw, this adds a watermark in the corner,
both for branding purposes and as an incentive for our enterprise
customers to purchase a license.
For commercial usage of tldraw, you add a license to the `<Tldraw
licenseKey={YOUR_LICENSE_KEY} />` component so that the watermark
doesn't show.
The license is a signed key that has various bits of information in it,
such as:
- license type
- hosts that the license is valid for
- whether it's an internal-only license
- expiry date
We check the license on load and show a watermark (or throw an error if
internal-only) if the license is not valid in a production environment.
This is a @MitjaBezensek, @Taha-Hassan-Git, @mimecuvalo joint
production! 🤜 🤛
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [x] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. We will be dogfooding on staging.tldraw.com and tldraw.com itself
before releasing this.
### Release Notes
- SDK: wires up tldraw to have licensing mechanisms.
---------
Co-authored-by: Mitja Bezenšek <mitja.bezensek@gmail.com>
Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com>
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
2024-07-11 11:49:18 +00:00
|
|
|
const publishDates = getNewPublishDates(packageVersion)
|
|
|
|
const file = `export const version = '${packageVersion}'
|
|
|
|
export const publishDates = {
|
|
|
|
major: '${publishDates.major}',
|
|
|
|
minor: '${publishDates.minor}',
|
|
|
|
patch: '${publishDates.patch}',
|
|
|
|
}`
|
2024-03-11 17:52:58 +00:00
|
|
|
|
2024-02-26 18:30:55 +00:00
|
|
|
await writeCodeFile(
|
|
|
|
'scripts/refresh-assets.ts',
|
|
|
|
'typescript',
|
|
|
|
join(REPO_ROOT, 'apps', 'dotcom', 'version.ts'),
|
|
|
|
file
|
|
|
|
)
|
|
|
|
await writeCodeFile(
|
|
|
|
'scripts/refresh-assets.ts',
|
|
|
|
'typescript',
|
|
|
|
join(REPO_ROOT, 'packages', 'editor', 'src', 'version.ts'),
|
|
|
|
file
|
|
|
|
)
|
|
|
|
await writeCodeFile(
|
|
|
|
'scripts/refresh-assets.ts',
|
|
|
|
'typescript',
|
|
|
|
join(REPO_ROOT, 'packages', 'tldraw', 'src', 'lib', 'ui', 'version.ts'),
|
|
|
|
file
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-07-15 11:03:11 +00:00
|
|
|
class Code {
|
|
|
|
protected parts: (string | { toString(): string })[] = []
|
|
|
|
|
|
|
|
toString() {
|
|
|
|
return this.parts.map((part) => (typeof part === 'string' ? part : part.toString())).join('')
|
|
|
|
}
|
|
|
|
|
|
|
|
append(...parts: string[]): this {
|
|
|
|
for (const part of parts) {
|
|
|
|
this.parts.push(part + '\n')
|
|
|
|
}
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
}
|
|
|
|
class CodeFile extends Code {
|
|
|
|
private imports = new Map<string, string>()
|
|
|
|
|
2024-07-16 10:41:55 +00:00
|
|
|
constructor(private header: string = '') {
|
2024-07-15 11:03:11 +00:00
|
|
|
super()
|
|
|
|
}
|
|
|
|
|
|
|
|
override toString(): string {
|
|
|
|
return [
|
|
|
|
this.header,
|
|
|
|
Array.from(this.imports, ([file, name]) => {
|
|
|
|
return `import ${name} from ${JSON.stringify(file)};`
|
|
|
|
}).join('\n'),
|
|
|
|
super.toString(),
|
|
|
|
].join('\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
import(file: string) {
|
|
|
|
let name = this.imports.get(file)
|
|
|
|
if (!name) {
|
|
|
|
name = this.getName(file)
|
|
|
|
this.imports.set(file, name)
|
|
|
|
}
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
2024-07-16 10:41:55 +00:00
|
|
|
formatName(name: string) {
|
|
|
|
return `$_${name.replace(/\W+/g, '_')}`
|
2024-07-15 11:03:11 +00:00
|
|
|
.replace(/^\$_+(\D)/, (_, s) => s.toLowerCase())
|
|
|
|
.replace(/_+(.)/g, (_, s) => s.toUpperCase())
|
2024-07-16 10:41:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getName(name: string, suffix?: number): string {
|
|
|
|
const formatted = this.formatName(`${name}${suffix ?? ''}`)
|
2024-07-15 11:03:11 +00:00
|
|
|
|
2024-07-16 10:41:55 +00:00
|
|
|
if (this.toString().match(formatted)) {
|
2024-07-15 11:03:11 +00:00
|
|
|
return this.getName(name, (suffix ?? 1) + 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
return formatted
|
|
|
|
}
|
|
|
|
|
|
|
|
appendFn(header: string): CodeFunction {
|
|
|
|
const fn = new CodeFunction(this, header)
|
|
|
|
this.parts.push(fn)
|
|
|
|
return fn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class CodeFunction extends Code {
|
|
|
|
constructor(
|
|
|
|
private file: CodeFile,
|
|
|
|
private header: string
|
|
|
|
) {
|
|
|
|
super()
|
|
|
|
}
|
|
|
|
|
|
|
|
private consts = new Map<string, string>()
|
|
|
|
|
|
|
|
override toString(): string {
|
|
|
|
let body = super.toString()
|
|
|
|
|
|
|
|
const lines = [this.header]
|
|
|
|
for (const [name, expr] of this.consts) {
|
|
|
|
const firstFoundIndex = body.indexOf(name)
|
|
|
|
const secondFoundIndex = body.indexOf(name, firstFoundIndex + 1)
|
|
|
|
if (secondFoundIndex === -1) {
|
|
|
|
// only exists once, we don't need to declare it:
|
|
|
|
body = body.replace(name, expr)
|
|
|
|
} else {
|
|
|
|
lines.push(`const ${name} = ${expr};`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lines.push(body)
|
|
|
|
lines.push('}')
|
|
|
|
return lines.join('\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
private memos = new Map<unknown, string>()
|
|
|
|
memo(key: string, expr: string) {
|
|
|
|
const existing = this.memos.get(key)
|
|
|
|
if (existing) return existing
|
|
|
|
|
|
|
|
const varName = this.file.getName(key)
|
|
|
|
this.consts.set(varName, expr)
|
|
|
|
this.memos.set(key, varName)
|
|
|
|
return varName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-25 11:01:25 +00:00
|
|
|
// --- RUN
|
|
|
|
async function main() {
|
2023-06-01 18:01:49 +00:00
|
|
|
nicelog('Copying icons...')
|
2023-04-25 11:01:25 +00:00
|
|
|
await copyIcons()
|
2023-06-01 18:01:49 +00:00
|
|
|
nicelog('Copying embed icons...')
|
2023-04-25 11:01:25 +00:00
|
|
|
await copyEmbedIcons()
|
2023-06-01 18:01:49 +00:00
|
|
|
nicelog('Copying fonts...')
|
2023-04-25 11:01:25 +00:00
|
|
|
await copyFonts()
|
2023-06-01 18:01:49 +00:00
|
|
|
nicelog('Copying translations...')
|
2023-04-25 11:01:25 +00:00
|
|
|
await copyTranslations()
|
sdk: wires up tldraw to have licensing mechanisms (#4021)
For non-commercial usage of tldraw, this adds a watermark in the corner,
both for branding purposes and as an incentive for our enterprise
customers to purchase a license.
For commercial usage of tldraw, you add a license to the `<Tldraw
licenseKey={YOUR_LICENSE_KEY} />` component so that the watermark
doesn't show.
The license is a signed key that has various bits of information in it,
such as:
- license type
- hosts that the license is valid for
- whether it's an internal-only license
- expiry date
We check the license on load and show a watermark (or throw an error if
internal-only) if the license is not valid in a production environment.
This is a @MitjaBezensek, @Taha-Hassan-Git, @mimecuvalo joint
production! 🤜 🤛
### Change Type
<!-- ❗ Please select a 'Scope' label ❗️ -->
- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff
<!-- ❗ Please select a 'Type' label ❗️ -->
- [ ] `bugfix` — Bug fix
- [x] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
### Test Plan
1. We will be dogfooding on staging.tldraw.com and tldraw.com itself
before releasing this.
### Release Notes
- SDK: wires up tldraw to have licensing mechanisms.
---------
Co-authored-by: Mitja Bezenšek <mitja.bezensek@gmail.com>
Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com>
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
2024-07-11 11:49:18 +00:00
|
|
|
nicelog('Copying watermarks...')
|
|
|
|
await copyWatermarks()
|
2023-06-01 18:01:49 +00:00
|
|
|
nicelog('Writing asset declaration file...')
|
2023-06-09 11:43:01 +00:00
|
|
|
await writeAssetDeclarationDTSFile()
|
2023-05-05 13:10:36 +00:00
|
|
|
await writeUrlBasedAssetDeclarationFile()
|
2024-01-19 15:31:01 +00:00
|
|
|
await writeImportBasedAssetDeclarationFile('', 'imports.js')
|
|
|
|
await writeImportBasedAssetDeclarationFile('?url', 'imports.vite.js')
|
2023-06-09 11:43:01 +00:00
|
|
|
await writeSelfHostedAssetDeclarationFile()
|
2024-02-26 18:30:55 +00:00
|
|
|
await copyVersionToDotCom()
|
2023-06-01 18:01:49 +00:00
|
|
|
nicelog('Done!')
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
main()
|