diff --git a/.circleci/config.yml b/.circleci/config.yml index ac280298c3..e2e90d64d5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,5 +1,60 @@ version: 2.1 + jobs: + lint: + docker: + - image: penpotapp/devenv:latest + + working_directory: ~/repo + resource_class: medium+ + + steps: + - checkout + + - run: + name: "fmt check" + working_directory: "." + command: | + yarn install + yarn run fmt:clj:check + + - run: + name: "lint clj common" + working_directory: "." + command: | + yarn run lint:clj:common + + - run: + name: "lint clj frontend" + working_directory: "." + command: | + yarn run lint:clj:frontend + + - run: + name: "lint clj backend" + working_directory: "." + command: | + yarn run lint:clj:backend + + - run: + name: "lint clj exporter" + working_directory: "." + command: | + yarn run lint:clj:exporter + + - run: + name: "lint clj library" + working_directory: "." + command: | + yarn run lint:clj:library + + - run: + name: "lint scss on frontend" + working_directory: "./frontend" + command: | + yarn install + yarn run lint:scss + test-common: docker: - image: penpotapp/devenv:latest @@ -19,14 +74,6 @@ jobs: keys: - v1-dependencies-{{ checksum "common/deps.edn"}} - - run: - name: "fmt check & linter" - working_directory: "./common" - command: | - yarn install - yarn run fmt:clj:check - yarn run lint:clj - - run: name: "JVM tests" working_directory: "./common" @@ -37,6 +84,7 @@ jobs: name: "NODE tests" working_directory: "./common" command: | + yarn install yarn run test - save_cache: @@ -63,23 +111,6 @@ jobs: keys: - v1-dependencies-{{ checksum "frontend/deps.edn"}} - - run: - name: "prepopulate linter cache" - working_directory: "./common" - command: | - yarn install - yarn run lint:clj - - - run: - name: "fmt check & linter" - working_directory: "./frontend" - command: | - yarn install - yarn run fmt:clj:check - yarn run fmt:js:check - yarn run lint:scss - yarn run lint:clj - - run: name: "unit tests" working_directory: "./frontend" @@ -115,8 +146,8 @@ jobs: name: Install dependencies working_directory: "./frontend" command: | - yarn - npx playwright install --with-deps + yarn install + yarn run playwright install --with-deps chromium - run: name: Build Storybook @@ -185,21 +216,6 @@ jobs: keys: - v1-dependencies-{{ checksum "backend/deps.edn" }} - - run: - name: "prepopulate linter cache" - working_directory: "./common" - command: | - yarn install - yarn run lint:clj - - - run: - name: "fmt check & linter" - working_directory: "./backend" - command: | - yarn install - yarn run fmt:clj:check - yarn run lint:clj - - run: name: "tests" working_directory: "./backend" @@ -217,35 +233,6 @@ jobs: - ~/.m2 key: v1-dependencies-{{ checksum "backend/deps.edn" }} - test-exporter: - docker: - - image: penpotapp/devenv:latest - - working_directory: ~/repo - resource_class: medium+ - - environment: - JAVA_OPTS: -Xmx4g -Xms100m -XX:+UseSerialGC - NODE_OPTIONS: --max-old-space-size=4096 - - steps: - - checkout - - - run: - name: "prepopulate linter cache" - working_directory: "./common" - command: | - yarn install - yarn run lint:clj - - - run: - name: "fmt check & linter" - working_directory: "./exporter" - command: | - yarn install - yarn run fmt:clj:check - yarn run lint:clj - test-render-wasm: docker: - image: penpotapp/devenv:latest @@ -278,10 +265,25 @@ jobs: workflows: penpot: jobs: - - test-frontend - - test-components - - test-integration - - test-backend - - test-common - - test-exporter + - lint + - test-frontend: + requires: + - lint: success + + - test-components: + requires: + - lint: success + + - test-integration: + requires: + - lint: success + + - test-backend: + requires: + - lint: success + + - test-common: + requires: + - lint: success + - test-render-wasm diff --git a/common/src/app/common/files/builder.cljc b/common/src/app/common/files/builder.cljc index 19e6681930..56ad3fa903 100644 --- a/common/src/app/common/files/builder.cljc +++ b/common/src/app/common/files/builder.cljc @@ -10,7 +10,7 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.common.features :as cfeat] + ;; [app.common.features :as cfeat] [app.common.files.changes :as ch] [app.common.files.migrations :as fmig] [app.common.geom.shapes :as gsh] @@ -133,20 +133,6 @@ (def decode-add-component (sm/decode-fn schema:add-component sm/json-transformer)) -(def schema:add-component-instance - [:map - [:component-id ::sm/uuid] - [:file-id {:optional true} ::sm/uuid] - [:frame-id {:optional true} ::sm/uuid] - [:page-id {:optional true} ::sm/uuid]]) - -(def ^:private check-add-component-instance - (sm/check-fn schema:add-component-instance - :hint "invalid arguments passed for add-component-instance")) - -(def decode-add-component-instance - (sm/decode-fn schema:add-component-instance sm/json-transformer)) - (def schema:add-bool [:map [:group-id ::sm/uuid] @@ -198,11 +184,31 @@ (-> (get-current-objects state) (get shape-id))) +;; WORKAROUND: A copy of features from staging for make the library +;; generate files compatible with version released right now. This +;; should be removed and replaced with cfeat/default-features when 2.8 +;; version is released + +(def default-features + #{"fdata/shape-data-type" + "styles/v2" + "layout/grid" + "components/v2" + "plugins/runtime" + "design-tokens/v1"}) + +;; WORKAROUND: the same as features +(def available-migrations + (-> fmig/available-migrations + (disj "003-convert-path-content") + (disj "0002-clean-shape-interactions") + (disj "0003-fix-root-shape"))) + (defn add-file [state params] (let [params (-> params - (assoc :features cfeat/default-features) - (assoc :migrations fmig/available-migrations) + (assoc :features default-features) + (assoc :migrations available-migrations) (update :id default-uuid)) file (types.file/make-file params :create-page false)] (-> state @@ -432,33 +438,6 @@ (commit-change change1) (commit-change change2)))) - -(defn add-component-instance - [state params] - - (let [{:keys [component-id file-id frame-id page-id]} - (check-add-component-instance params) - - file-id - (or file-id (get state ::current-file-id)) - - frame-id - (or frame-id (get state ::current-frame-id)) - - page-id - (or page-id (get state ::current-page-id)) - - change - {:type :mod-obj - :id frame-id - :page-id page-id - :operations - [{:type :set :attr :component-root :val true} - {:type :set :attr :component-id :val component-id} - {:type :set :attr :component-file :val file-id}]}] - - (commit-change state change))) - (defn delete-shape [file id] (commit-change diff --git a/library/CHANGES.md b/library/CHANGES.md new file mode 100644 index 0000000000..80f1b50ca9 --- /dev/null +++ b/library/CHANGES.md @@ -0,0 +1,12 @@ +# CHANGELOG + +## 1.0.1 + +- Make the library generate a .penpot file compatible with penpot 2.7.x +- Remove useless method `addComponentInstance` + + +## 1.0.0 + +- Initial release after big refactor (from the first MVP prototype) + diff --git a/library/package.json b/library/package.json index de2605bda9..c3ac96ac45 100644 --- a/library/package.json +++ b/library/package.json @@ -1,6 +1,6 @@ { "name": "@penpot/library", - "version": "1.0.0", + "version": "1.0.1", "license": "MPL-2.0", "author": "Kaleidos INC", "packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538", diff --git a/library/playground/sample2.js b/library/playground/sample2.js new file mode 100644 index 0000000000..96cccde0d4 --- /dev/null +++ b/library/playground/sample2.js @@ -0,0 +1,106 @@ +import * as penpot from "#self"; +import { writeFile, readFile } from "fs/promises"; +import { createWriteStream } from "fs"; +import { Writable } from "stream"; + +// console.log(penpot); + +(async function () { + const context = penpot.createBuildContext(); + + { + context.addFile({ name: "Test File 1" }); + context.addPage({ name: "Foo Page" }); + + // Add image media + const buffer = await readFile("./playground/sample.jpg"); + const blob = new Blob([buffer], { type: "image/jpeg" }); + + const mediaId = context.addFileMedia( + { + name: "avatar.jpg", + width: 512, + height: 512, + }, + blob + ); + + // Add image color asset + const assetColorId = context.addLibraryColor({ + name: "Avatar", + opacity: 1, + image: { + ...context.getMediaAsImage(mediaId), + keepAspectRatio: true, + }, + }); + + const boardId = context.addBoard({ + name: "Foo Board", + x: 0, + y: 0, + width: 500, + height: 1000, + }); + + const fill = { + fillColorRefId: assetColorId, + fillColorRefFile: context.currentFileId, + fillImage: { + ...context.getMediaAsImage(mediaId), + keepAspectRatio: true, + }, + }; + + const stroke = { + strokeColorRefId: assetColorId, + strokeColorRefFile: context.currentFileId, + strokeWidth: 48, + strokeAlignment: "inner", + strokeStyle: "solid", + strokeOpacity: 1, + strokeImage: { + ...context.getMediaAsImage(mediaId), + keepAspectRatio: true, + }, + }; + + context.addRect({ + name: "Rect 1", + x: 20, + y: 20, + width: 500, + height: 1000, + fills: [fill], + strokes: [stroke], + }); + + context.closeBoard(); + context.closeFile(); + } + + { + let result = await penpot.exportAsBytes(context); + await writeFile("sample-sync.zip", result); + } + + // { + // // Create a file stream to write the zip to + // const output = createWriteStream('sample-stream.zip'); + // // Wrap Node's stream in a WHATWG WritableStream + // const writable = Writable.toWeb(output); + // await penpot.exportStream(context, writable); + // } +})() + .catch((cause) => { + console.error(cause); + + const innerCause = cause.cause; + if (innerCause) { + console.error("Inner cause:", innerCause); + } + process.exit(-1); + }) + .finally(() => { + process.exit(0); + }); diff --git a/library/playground/sample3.js b/library/playground/sample3.js new file mode 100644 index 0000000000..df35e6d8cd --- /dev/null +++ b/library/playground/sample3.js @@ -0,0 +1,68 @@ +import * as penpot from "#self"; +import { writeFile, readFile } from "fs/promises"; + +(async function () { + const context = penpot.createBuildContext(); + + { + context.addFile({ name: "Test File 1" }); + context.addPage({ name: "Foo Page" }); + + const pathContent = [ + { + "command": "move-to", + "params": { + "x": 480.0, + "y": 839.0 + } + }, + { + "command": "line-to", + "params": { + "x": 439.0, + "y": 802.0 + } + }, + { + "command": "curve-to", + "params": { + "c1x": 368.0, + "c1y": 737.0, + "c2x": 310.0, + "c2y": 681.0, + "x": 264.0, + "y": 634.0 + } + }, + { + "command": "close-path", + "params": {} + } + ]; + + context.addPath({ + name: "Path 1", + content: pathContent + }); + + context.closeBoard(); + context.closeFile(); + } + + { + let result = await penpot.exportAsBytes(context); + await writeFile("sample-path.zip", result); + } +})() + .catch((cause) => { + console.error(cause); + + const innerCause = cause.cause; + if (innerCause) { + console.error("Inner cause:", innerCause); + } + process.exit(-1); + }) + .finally(() => { + process.exit(0); + }); diff --git a/library/src/lib/builder.cljs b/library/src/lib/builder.cljs index df9b909cb3..f77f6f8ae5 100644 --- a/library/src/lib/builder.cljs +++ b/library/src/lib/builder.cljs @@ -230,16 +230,6 @@ (catch :default cause (handle-exception cause)))) - :addComponentInstance - (fn [params] - (try - (let [params (-> (decode-params params) - (fb/decode-add-component-instance))] - (-> (swap! state fb/add-component-instance params) - (get-last-id))) - (catch :default cause - (handle-exception cause)))) - :addFileMedia (fn [params blob] diff --git a/library/src/lib/export.cljs b/library/src/lib/export.cljs index f2b7fec266..5d9ee3efa0 100644 --- a/library/src/lib/export.cljs +++ b/library/src/lib/export.cljs @@ -82,6 +82,14 @@ :is-shared :version}) +(defn- encode-shape* + [{:keys [type] :as shape}] + (let [shape (if (or (= type :path) + (= type :bool)) + (update shape :content vec) + shape)] + (-> shape encode-shape json/encode))) + (defn- generate-file-export-procs [{:keys [id data] :as file}] (cons @@ -109,7 +117,7 @@ (map (fn [[shape-id shape]] (let [shape (assoc shape :page-id page-id)] [(str "files/" id "/pages/" page-id "/" shape-id ".json") - (delay (-> shape encode-shape json/encode))])) + (delay (encode-shape* shape))])) objects))))))) (->> (get data :components)