Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
74c4608
Bump lodash from 4.17.21 to 4.17.23
dependabot[bot] Jan 23, 2026
0d055c8
Make comparison to slug column case insensitive
Nick-Lucas Jan 28, 2026
899dcd8
Add code link Plugin and CLI
huntercaron Dec 17, 2025
6d1ae45
Fixing lint / formatting errors
huntercaron Dec 17, 2025
159b380
eslint fixes
huntercaron Dec 17, 2025
8b8ac04
Update tests
huntercaron Dec 17, 2025
60ebe0a
More lint/ts fixes
huntercaron Dec 17, 2025
aa0858c
No more auto-verbose logging
huntercaron Dec 17, 2025
6eda437
Fix publishing by bundling shared package
huntercaron Dec 17, 2025
d47d7f8
Dark mode
huntercaron Dec 18, 2025
a34ee36
Cleanup api.ts
huntercaron Dec 18, 2025
b821688
Update tests
huntercaron Dec 18, 2025
8d9af20
Cleanup
huntercaron Jan 14, 2026
89a006b
Lockfile fixes from rebase
huntercaron Jan 14, 2026
2cd2f4c
Updating types and controller tests
huntercaron Jan 14, 2026
336c728
Biome formatting
huntercaron Jan 14, 2026
5a483cd
Cleanup
huntercaron Jan 15, 2026
6a25207
Better handling for jsdeliver errors
huntercaron Jan 15, 2026
3b45d10
Publish
huntercaron Jan 15, 2026
6eb1f74
Switch to ts import extensions
huntercaron Feb 2, 2026
4733542
Update html title
huntercaron Feb 2, 2026
5092fa7
Upgrade deps
huntercaron Feb 2, 2026
7083487
Improve conflict detection
huntercaron Feb 3, 2026
d19f10d
Remove unnecessary string cast
huntercaron Feb 3, 2026
b27028e
Add iconi
huntercaron Feb 3, 2026
c9a9123
Rework file deletions
huntercaron Feb 3, 2026
994c09a
Typesafe file content restoration
huntercaron Feb 3, 2026
0acf723
Remove unused queue logic
huntercaron Feb 3, 2026
21b5802
Release
huntercaron Feb 3, 2026
7dd77d4
Add asset and readme
huntercaron Feb 4, 2026
43b2d21
Move framer-plugin-tools into monorepo as workspace package
niekert Feb 5, 2026
999cafa
node22 target
niekert Feb 5, 2026
7e2bedc
Add tests for packPlugin
niekert Feb 5, 2026
d28c187
rename to zipPluginDistribution
niekert Feb 5, 2026
e789f38
support relative and absolute paths
niekert Feb 5, 2026
3d1d28e
Add plugin submission workflow and GH integrations
niekert Feb 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

This pull request …

### Changelog

<!-- Required when using the "Submit on merge" label. Describe user-facing changes in bullet points. -->

-

### Testing

<!-- List of steps to verify the code this pull request changed. If it is a Plugin additions, what are the core workflows to test. If it is a bug fix, what are the steps to verify it is fixed -->
Expand Down
42 changes: 21 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,38 +93,38 @@ jobs:
uses: tj-actions/changed-files@v45
with:
files: |
plugins/*/src/**
plugins/*/test/**
plugins/*/*.test.ts
plugins/*/*.test.tsx
plugins/*/vitest.config.ts
plugins/*/test-setup.ts
{plugins,packages}/*/src/**
{plugins,packages}/*/test/**
{plugins,packages}/*/*.test.ts
{plugins,packages}/*/*.test.tsx
{plugins,packages}/*/vitest.config.ts
{plugins,packages}/*/test-setup.ts

- name: Install dependencies
if: steps.changed-files.outputs.any_changed == 'true'
run: yarn

- name: Get changed plugins
- name: Get changed workspaces
if: steps.changed-files.outputs.any_changed == 'true'
id: changed-plugins
id: changed-workspaces
run: |
# Extract unique plugin names from changed files
PLUGINS=$(echo "${{ steps.changed-files.outputs.all_changed_files }}" | \
grep -E '^plugins/[^/]+/' | \
cut -d'/' -f2 | \
# Extract unique workspace paths from changed files
WORKSPACES=$(echo "${{ steps.changed-files.outputs.all_changed_files }}" | \
grep -oE '(plugins|packages)/[^/]+' | \
sort -u | \
tr '\n' ' ')
echo "plugins=$PLUGINS" >> $GITHUB_OUTPUT
echo "Changed plugins: $PLUGINS"
echo "workspaces=$WORKSPACES" >> $GITHUB_OUTPUT
echo "Changed workspaces: $WORKSPACES"

- name: Run tests for changed plugins
if: steps.changed-files.outputs.any_changed == 'true' && steps.changed-plugins.outputs.plugins != ''
- name: Run tests for changed workspaces
if: steps.changed-files.outputs.any_changed == 'true' && steps.changed-workspaces.outputs.workspaces != ''
run: |
for plugin in ${{ steps.changed-plugins.outputs.plugins }}; do
echo "Checking tests for plugin: $plugin"
if [ -f "plugins/$plugin/package.json" ] && grep -q '"check-vitest"' "plugins/$plugin/package.json"; then
yarn workspace $plugin check-vitest
for workspace in ${{ steps.changed-workspaces.outputs.workspaces }}; do
echo "Checking tests for workspace: $workspace"
if [ -f "$workspace/package.json" ] && grep -q '"check-vitest"' "$workspace/package.json"; then
name=$(jq -r '.name' "$workspace/package.json")
yarn workspace "$name" check-vitest
else
echo "No check-vitest script found for $plugin, skipping..."
echo "No check-vitest script found for $workspace, skipping..."
fi
done
33 changes: 28 additions & 5 deletions .github/workflows/shippy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ on:
- edited
- ready_for_review
- synchronize
- labeled
- unlabeled
workflow_dispatch:
# NOTE: To prevent GitHub from adding PRs to the merge queue before check is done,
# make sure that there is a ruleset that requires the “Shippy check to pass.
Expand All @@ -24,11 +26,32 @@ jobs:
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false && github.event.pull_request.user.login != 'dependabot[bot]'
steps:
- name: Check PR description
- name: Check if Submit on merge label is present
id: check-label
uses: actions/github-script@v7
with:
script: |
const prBody = context.payload.pull_request.body?.trim()
if (!prBody) {
core.setFailed("❌ PR description is required.")
}
const labels = context.payload.pull_request.labels || []
const hasSubmitLabel = labels.some(label => label.name === 'Submit on merge')
core.setOutput('require_changelog', hasSubmitLabel ? 'true' : 'false')

- name: Checkout repository
uses: actions/checkout@v4
with:
sparse-checkout: |
scripts
package.json
yarn.lock
.yarnrc.yml
.yarn
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sparse checkout missing required .tool-versions file

High Severity

The sparse checkout configuration does not include .tool-versions, but the Setup Node.js step on line 51 requires this file via node-version-file: .tool-versions. The workflow will fail because the file won't exist after the sparse checkout completes.

Fix in Cursor Fix in Web


- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .tool-versions

- name: Validate PR body
run: yarn dlx tsx scripts/validate-pr-body.ts
env:
PR_BODY: ${{ github.event.pull_request.body }}
REQUIRE_CHANGELOG: ${{ steps.check-label.outputs.require_changelog }}
64 changes: 64 additions & 0 deletions .github/workflows/submit-on-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Submit on Merge

on:
pull_request:
types:
- closed
branches:
- main

jobs:
submit:
name: Submit Changed Plugins
runs-on: ubuntu-latest
# Only run if PR was merged (not just closed) and has "Submit on merge" label
if: |
github.event.pull_request.merged == true &&
contains(github.event.pull_request.labels.*.name, 'Submit on merge')
# FIXME: Should be production
environment: development
Copy link
Member Author

@niekert niekert Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note to self to fix before merging.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Development environment hardcoded instead of production

High Severity

The workflow has environment: development and FRAMER_ENV: development hardcoded with FIXME comments. The PR notes explicitly state "note to self to fix before merging." If merged as-is, all plugins submitted via the "Submit on merge" flow would go to the development environment instead of production.

Additional Locations (1)

Fix in Cursor Fix in Web


steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for git tags and diff

- name: Configure git identity
run: |
git config --global user.email "marketplace@framer.team"
git config --global user.name "Framer Marketplace"

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .tool-versions

- name: Install dependencies
run: yarn install

- name: Build framer-plugin-tools
working-directory: packages/plugin-tools
run: yarn build

- name: Write PR body to file
run: cat <<< "$PR_BODY" > /tmp/pr-body.txt
env:
PR_BODY: ${{ github.event.pull_request.body }}

- name: Submit changed plugins
run: |
export CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }})
yarn tsx scripts/submit-on-merge.ts
env:
DEBUG: "1"
PR_BODY_FILE: /tmp/pr-body.txt
SESSION_TOKEN: ${{ secrets.SESSION_TOKEN }}
FRAMER_ADMIN_SECRET: ${{ secrets.FRAMER_ADMIN_SECRET }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_ERROR_WEBHOOK_URL: ${{ secrets.SLACK_ERROR_WEBHOOK_URL }}
RETOOL_URL: ${{ secrets.RETOOL_URL }}
# FIXME: Should be production
FRAMER_ENV: development
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should fix

GITHUB_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
124 changes: 124 additions & 0 deletions .github/workflows/submit-plugin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
name: Submit Plugin

on:
# Manual trigger from GitHub UI
workflow_dispatch:
inputs:
plugin_path:
description: 'Plugin directory (e.g., plugins/csv-import)'
required: true
type: string
changelog:
description: 'Changelog for this release'
required: true
type: string
environment:
description: 'Environment (development/production)'
required: true
default: 'development'
type: choice
options:
- development
- production
dry_run:
description: 'Dry run (skip submission and tagging)'
required: false
default: false
type: boolean

# Reusable workflow - can be called from other repos (e.g., framer/workshop)
workflow_call:
inputs:
plugin_path:
description: 'Plugin directory (e.g., plugins/csv-import)'
required: true
type: string
changelog:
description: 'Changelog for this release'
required: true
type: string
environment:
description: 'Environment (development/production)'
required: true
default: 'development'
type: string
dry_run:
description: 'Dry run (skip submission and tagging)'
required: false
default: false
type: boolean
secrets:
SESSION_TOKEN:
description: 'Framer session cookie'
required: true
FRAMER_ADMIN_SECRET:
description: 'Framer admin API key'
required: true
SLACK_WEBHOOK_URL:
description: 'Slack webhook URL for notifications'
required: false
RETOOL_URL:
description: 'Retool dashboard URL for Slack notifications'
required: false
SLACK_ERROR_WEBHOOK_URL:
description: 'Slack webhook URL for error notifications'
required: false

jobs:
submit:
name: Submit Plugin to Marketplace
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for git tags and diff

- name: Configure git identity
run: |
git config --global user.email "marketplace@framer.team"
git config --global user.name "Framer Marketplace"
- name: Validate plugin path
run: |
if [ ! -d "${{ github.workspace }}/${{ inputs.plugin_path }}" ]; then
echo "Error: Plugin path '${{ inputs.plugin_path }}' does not exist"
echo ""
echo "Available plugins:"
ls -1 plugins/
exit 1
fi
if [ ! -f "${{ github.workspace }}/${{ inputs.plugin_path }}/framer.json" ]; then
echo "Error: No framer.json found in '${{ inputs.plugin_path }}'"
exit 1
fi
echo "Plugin path validated: ${{ inputs.plugin_path }}"
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .tool-versions

- name: Install dependencies
run: yarn install

- name: Build framer-plugin-tools
run: yarn turbo run build --filter=framer-plugin-tools

- name: Submit plugin
run: yarn tsx scripts/submit-plugin.ts

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Checkout the workflow repository before running local scripts

For workflow_call, this job currently checks out the caller repository by default, but then executes scripts/submit-plugin.ts from the workspace. That means cross-repo callers (the stated use case) fail unless they coincidentally contain this repo’s scripts/ layout and framer-plugin-tools workspace package. Add an explicit checkout of this repository (or otherwise source the script/tooling) before invoking these commands.

Useful? React with 👍 / 👎.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will focus on cross repo invoking in follow up but good call

env:
PLUGIN_PATH: ${{ github.workspace }}/${{ inputs.plugin_path }}
REPO_ROOT: ${{ github.workspace }}
CHANGELOG: ${{ inputs.changelog }}
SESSION_TOKEN: ${{ secrets.SESSION_TOKEN }}
FRAMER_ADMIN_SECRET: ${{ secrets.FRAMER_ADMIN_SECRET }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_ERROR_WEBHOOK_URL: ${{ secrets.SLACK_ERROR_WEBHOOK_URL }}
RETOOL_URL: ${{ secrets.RETOOL_URL }}
FRAMER_ENV: ${{ inputs.environment }}
GITHUB_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
DRY_RUN: ${{ inputs.dry_run }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added assets/code-link.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"plugins/*"
],
"scripts": {
"test:scripts": "vitest run scripts/",
"check": "turbo run --continue check-biome check-eslint check-prettier check-svelte check-typescript check-vitest",
"dev": "turbo run dev --concurrency=40",
"fix-biome": "turbo run --continue check-biome -- --write",
Expand All @@ -22,16 +23,22 @@
"g:dev": "cd $INIT_CWD && run g:vite",
"g:preview": "cd $INIT_CWD && run g:vite preview",
"g:vite": "cd $INIT_CWD && NODE_OPTIONS='--no-warnings=ExperimentalWarning' vite --config ${VITE_CONFIG_PATH:-$PROJECT_CWD/packages/vite-config/src/index.ts}",
"g:pack": "cd $INIT_CWD && framer-plugin-tools pack",
"preview": "turbo run preview --concurrency=40"
},
"devDependencies": {
"@biomejs/biome": "^2.2.4",
"@framer/eslint-config": "workspace:*",
"@framer/vite-config": "workspace:*",
"@types/node": "^22.15.21",
"eslint": "^9.35.0",
"framer-plugin-tools": "workspace:*",
"jiti": "^2.5.1",
"tsx": "^4.19.0",
"turbo": "^2.5.6",
"typescript": "^5.9.2",
"vite": "^7.1.11"
"valibot": "^1.2.0",
"vite": "^7.1.11",
"vitest": "^3.2.4"
}
}
3 changes: 3 additions & 0 deletions packages/code-link-cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Framer Code Link CLI

Two-way syncing Framer of code components between Framer and your computer.
40 changes: 40 additions & 0 deletions packages/code-link-cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "framer-code-link",
"version": "0.7.0",
"description": "CLI tool for syncing Framer code components - controller-centric architecture",
"main": "dist/index.mjs",
"type": "module",
"bin": "./dist/index.mjs",
"files": [
"dist"
],
"scripts": {
"dev": "NODE_ENV=development tsx src/index.ts",
"build": "tsdown",
"start": "node dist/index.mjs",
"test": "vitest run"
},
"keywords": [
"framer",
"sync",
"code-components"
],
"author": "",
"license": "MIT",
"dependencies": {
"@typescript/ata": "^0.9.8",
"chokidar": "^5.0.0",
"commander": "^14.0.3",
"prettier": "^3.7.4",
"typescript": "^5.9.3",
"ws": "^8.18.3"
},
"devDependencies": {
"@code-link/shared": "workspace:*",
"@types/node": "^22.19.2",
"@types/ws": "^8.18.1",
"tsdown": "^0.20.1",
"tsx": "^4.21.0",
"vitest": "^4.0.15"
}
}
Loading