mirror of
https://github.com/webstudio-is/webstudio.git
synced 2025-03-14 09:57:02 +00:00
## Description We allow console.info (btw we should also allow warn/error/info), we only disallow console.log in the linter because that's what we use for debugging. ## Steps for reproduction 1. click button 2. expect xyz ## Code Review - [ ] hi @TrySound , I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 5de6) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env.example` and the `builder/env-check.js` if mandatory
220 lines
5.2 KiB
TypeScript
220 lines
5.2 KiB
TypeScript
import { z } from "zod";
|
|
import { PrismaClient } from "./client";
|
|
|
|
const Unit = z.string();
|
|
|
|
type Unit = z.infer<typeof Unit>;
|
|
|
|
const UnitValue = z.object({
|
|
type: z.literal("unit"),
|
|
unit: Unit,
|
|
value: z.number(),
|
|
});
|
|
|
|
type UnitValue = z.infer<typeof UnitValue>;
|
|
|
|
const KeywordValue = z.object({
|
|
type: z.literal("keyword"),
|
|
// @todo use exact type
|
|
value: z.string(),
|
|
});
|
|
type KeywordValue = z.infer<typeof KeywordValue>;
|
|
|
|
/**
|
|
* Valid unparsed css value
|
|
**/
|
|
const UnparsedValue = z.object({
|
|
type: z.literal("unparsed"),
|
|
value: z.string(),
|
|
});
|
|
|
|
const FontFamilyValue = z.object({
|
|
type: z.literal("fontFamily"),
|
|
value: z.array(z.string()),
|
|
});
|
|
type FontFamilyValue = z.infer<typeof FontFamilyValue>;
|
|
|
|
const RgbValue = z.object({
|
|
type: z.literal("rgb"),
|
|
r: z.number(),
|
|
g: z.number(),
|
|
b: z.number(),
|
|
alpha: z.number(),
|
|
});
|
|
type RgbValue = z.infer<typeof RgbValue>;
|
|
|
|
const ImageValue = z.object({
|
|
type: z.literal("image"),
|
|
value: z.object({ type: z.literal("asset"), value: z.unknown() }),
|
|
});
|
|
|
|
type ImageValue = z.infer<typeof ImageValue>;
|
|
|
|
// We want to be able to render the invalid value
|
|
// and show it is invalid visually, without saving it to the db
|
|
const InvalidValue = z.object({
|
|
type: z.literal("invalid"),
|
|
value: z.string(),
|
|
});
|
|
type InvalidValue = z.infer<typeof InvalidValue>;
|
|
|
|
const UnsetValue = z.object({
|
|
type: z.literal("unset"),
|
|
value: z.literal(""),
|
|
});
|
|
type UnsetValue = z.infer<typeof UnsetValue>;
|
|
|
|
/**
|
|
* Shared zod types with DB types.
|
|
* ImageValue in DB has a different type
|
|
*/
|
|
const SharedStaticStyleValue = z.union([
|
|
UnitValue,
|
|
KeywordValue,
|
|
FontFamilyValue,
|
|
RgbValue,
|
|
UnparsedValue,
|
|
]);
|
|
|
|
const ValidStaticStyleValue = z.union([ImageValue, SharedStaticStyleValue]);
|
|
|
|
type ValidStaticStyleValue = z.infer<typeof ValidStaticStyleValue>;
|
|
|
|
const VarValue = z.object({
|
|
type: z.literal("var"),
|
|
value: z.string(),
|
|
fallbacks: z.array(ValidStaticStyleValue),
|
|
});
|
|
type VarValue = z.infer<typeof VarValue>;
|
|
|
|
/**
|
|
* Shared types with DB types
|
|
*/
|
|
const SharedStyleValue = z.union([
|
|
SharedStaticStyleValue,
|
|
InvalidValue,
|
|
UnsetValue,
|
|
VarValue,
|
|
]);
|
|
|
|
const StoredImageValue = z.object({
|
|
type: z.literal("image"),
|
|
value: z.object({ type: z.literal("asset"), value: z.string() }),
|
|
});
|
|
|
|
const StoredLayersValue = z.object({
|
|
type: z.literal("layers"),
|
|
value: z.array(
|
|
z.union([
|
|
UnitValue,
|
|
KeywordValue,
|
|
UnparsedValue,
|
|
StoredImageValue,
|
|
InvalidValue,
|
|
])
|
|
),
|
|
});
|
|
|
|
export const StoredStyleDecl = z.object({
|
|
styleSourceId: z.string(),
|
|
breakpointId: z.string(),
|
|
// @todo can't figure out how to make property to be enum
|
|
property: z.string(),
|
|
value: z.union([StoredImageValue, StoredLayersValue, SharedStyleValue]),
|
|
});
|
|
|
|
type StoredStyleDecl = z.infer<typeof StoredStyleDecl>;
|
|
|
|
const layeredBackgroundProps = [
|
|
"backgroundAttachment",
|
|
"backgroundClip",
|
|
"backgroundBlendMode",
|
|
"backgroundImage",
|
|
"backgroundOrigin",
|
|
"backgroundPositionX",
|
|
"backgroundPositionY",
|
|
"backgroundRepeat",
|
|
"backgroundSize",
|
|
];
|
|
|
|
export default () => {
|
|
const client = new PrismaClient({
|
|
// Uncomment to see the queries in console as the migration runs
|
|
// log: ["query", "info", "warn", "error"],
|
|
});
|
|
return client.$transaction(
|
|
async (prisma) => {
|
|
let cursor: undefined | { id: string; projectId: string } = undefined;
|
|
let hasNext = true;
|
|
const chunkSize = 1000;
|
|
|
|
hasNext = true;
|
|
while (hasNext) {
|
|
const builds = await prisma.build.findMany({
|
|
take: chunkSize,
|
|
where: {
|
|
OR: [{ isDev: true }, { isProd: true }],
|
|
},
|
|
orderBy: {
|
|
id: "asc",
|
|
},
|
|
...(cursor
|
|
? {
|
|
skip: 1, // Skip the cursor
|
|
cursor: { id_projectId: cursor },
|
|
}
|
|
: null),
|
|
});
|
|
const lastBuild = builds.at(-1);
|
|
if (lastBuild) {
|
|
cursor = { id: lastBuild.id, projectId: lastBuild.projectId };
|
|
}
|
|
hasNext = builds.length === chunkSize;
|
|
|
|
const buildsToUpdate = [];
|
|
|
|
for (const build of builds) {
|
|
let hasConversion = false;
|
|
|
|
const styles: StoredStyleDecl[] = JSON.parse(build.styles);
|
|
|
|
for (const style of styles) {
|
|
if (layeredBackgroundProps.includes(style.property)) {
|
|
if (
|
|
style.value.type === "image" ||
|
|
style.value.type === "unit" ||
|
|
style.value.type === "unparsed" ||
|
|
style.value.type === "keyword"
|
|
) {
|
|
hasConversion = true;
|
|
|
|
style.value = {
|
|
type: "layers",
|
|
value: [style.value],
|
|
};
|
|
} else {
|
|
console.warn("unsupported style value", style.value);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasConversion) {
|
|
build.styles = JSON.stringify(styles);
|
|
buildsToUpdate.push(build as never);
|
|
}
|
|
}
|
|
|
|
await Promise.all(
|
|
buildsToUpdate.map(({ id, projectId, styles }) =>
|
|
prisma.build.update({
|
|
where: { id_projectId: { id, projectId } },
|
|
data: { styles },
|
|
})
|
|
)
|
|
);
|
|
}
|
|
},
|
|
{ timeout: 1000 * 60 * 8 }
|
|
);
|
|
};
|