tldraw/scripts/refresh-assets.ts

658 lines
18 KiB
TypeScript
Raw Normal View History

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,
REPO_ROOT,
writeCodeFile,
2023-04-25 11:01:25 +00:00
writeFile,
writeJsonFile,
writeStringFile,
} from './lib/file'
import { nicelog } from './lib/nicelog'
2023-04-25 11:01:25 +00:00
// We'll need to copy the assets into these folders
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',
'Shantell_Sans-Tldrawish': 'draw',
2023-04-25 11:01:25 +00:00
}
const ASSETS_FOLDER_PATH = join(REPO_ROOT, 'assets')
2023-04-25 11:01:25 +00:00
const collectedAssetUrls: Record<
'fonts' | 'icons' | 'translations' | 'embedIcons',
Record<string, { file: string; hash?: string }>
> = {
fonts: {},
icons: {},
translations: {},
embedIcons: {},
2023-04-25 11:01:25 +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 }
})
// 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 */
export const iconTypes = [
2023-04-25 11:01:25 +00:00
${icons.map((icon) => JSON.stringify(icon.replace('.svg', ''))).join(', ')}
] as const`
await writeCodeFile(
'scripts/refresh-assets.ts',
'typescript',
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', '')
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, '')
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) {
nicelog('Font mapping not found for', itemWithoutExtension)
2023-04-25 11:01:25 +00:00
process.exit(1)
}
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(
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-04-25 11:01:25 +00:00
const languagesSource = await readJsonIfExists(join(sourceFolderPath, 'languages.json'))!
interface Language {
label: string
locale: string
}
2023-04-25 11:01:25 +00:00
const languagesFile = `
/** @public */
export const LANGUAGES = ${JSON.stringify(
languagesSource.sort((a: Language, b: Language) => a.label.localeCompare(b.label, 'en'))
)} as const
2023-04-25 11:01:25 +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')
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)}
`
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}'`)
const translationKeysFilePath = join(uiPath, 'TLUiTranslationKey.ts')
2023-04-25 11:01:25 +00:00
const translationKeysFile = `
/** @public */
export type TLUiTranslationKey = ${translationKeys.join(' | ')}
2023-04-25 11:01:25 +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, '')
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)
)
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
}
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
async function writeUrlBasedAssetDeclarationFile() {
const codeFilePath = join(REPO_ROOT, 'packages', 'assets', 'urls.js')
const codeFile = new CodeFile(`
2023-04-25 11:01:25 +00:00
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="./modules.d.ts" />
import { formatAssetUrl } from './utils.js'
`)
const fn = codeFile.appendFn(`
/**
* @param {AssetUrlOptions} [opts]
* @public
*/
export function getAssetUrlsByMetaUrl(opts) {
`)
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
}
fn.append('},')
}
fn.append('}')
2023-04-25 11:01:25 +00:00
await writeCodeFile('scripts/refresh-assets.ts', 'javascript', codeFilePath, codeFile.toString())
2023-04-25 11:01:25 +00:00
}
async function writeImportBasedAssetDeclarationFile(
importSuffix: string,
fileName: string
): Promise<void> {
const codeFile = new CodeFile(`
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="./modules.d.ts" />
import { formatAssetUrl } from './utils.js'
`)
const fn = codeFile.appendFn(`
/**
* @param {AssetUrlOptions} [opts]
* @public
*/
export function getAssetUrlsByImport(opts) {
`)
fn.append('return {')
for (const [type, assets] of Object.entries(collectedAssetUrls)) {
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},`)
}
fn.append('},')
}
fn.append('}')
const codeFilePath = join(REPO_ROOT, 'packages', 'assets', fileName)
await writeCodeFile('scripts/refresh-assets.ts', 'javascript', codeFilePath, codeFile.toString())
}
async function writeSelfHostedAssetDeclarationFile(): Promise<void> {
const codeFilePath = join(REPO_ROOT, 'packages', 'assets', 'selfHosted.js')
const codeFile = new CodeFile(`
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="./modules.d.ts" />
import { formatAssetUrl } from './utils.js'
`)
const fn = codeFile.appendFn(`
/**
* @param {AssetUrlOptions} [opts]
* @public
*/
export function getAssetUrls(opts) {
`)
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},`)
}
fn.append('},')
}
fn.append('}')
await writeCodeFile('scripts/refresh-assets.ts', 'javascript', codeFilePath, codeFile.toString())
}
async function writeAssetDeclarationDTSFile() {
let dts = `
export type AssetUrl = string | { src: string }
export type AssetUrlOptions = { baseUrl?: string } | ((assetUrl: string) => string)
export type AssetUrls = {
`
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-04-25 11:01:25 +00:00
const assetDeclarationFilePath = join(REPO_ROOT, 'packages', 'assets', 'types.d.ts')
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
}
async function copyVersionToDotCom() {
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}',
}`
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
)
}
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>()
constructor(private header: string = '') {
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
}
formatName(name: string) {
return `$_${name.replace(/\W+/g, '_')}`
.replace(/^\$_+(\D)/, (_, s) => s.toLowerCase())
.replace(/_+(.)/g, (_, s) => s.toUpperCase())
}
getName(name: string, suffix?: number): string {
const formatted = this.formatName(`${name}${suffix ?? ''}`)
if (this.toString().match(formatted)) {
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() {
nicelog('Copying icons...')
2023-04-25 11:01:25 +00:00
await copyIcons()
nicelog('Copying embed icons...')
2023-04-25 11:01:25 +00:00
await copyEmbedIcons()
nicelog('Copying fonts...')
2023-04-25 11:01:25 +00:00
await copyFonts()
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()
nicelog('Writing asset declaration file...')
await writeAssetDeclarationDTSFile()
await writeUrlBasedAssetDeclarationFile()
await writeImportBasedAssetDeclarationFile('', 'imports.js')
await writeImportBasedAssetDeclarationFile('?url', 'imports.vite.js')
await writeSelfHostedAssetDeclarationFile()
await copyVersionToDotCom()
nicelog('Done!')
2023-04-25 11:01:25 +00:00
}
main()