From 3195b3c6d6248cf17d2412ad45a757756cf97074 Mon Sep 17 00:00:00 2001 From: Jacek Date: Fri, 16 Jan 2026 21:39:01 -0600 Subject: [PATCH 1/5] feat(upgrade): Add Vue-specific Protect to Show codemod MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vue SFC files require different prop syntax than React JSX: - React: `` → `` - Vue: `` → `` This adds a Vue-specific codemod that: - Transforms script imports (Protect → Show, ProtectProps → ShowProps) - Transforms template components with Vue v-bind syntax - Handles static and dynamic prop bindings - Handles condition callbacks - Only transforms when clerk imports are detected The core-3 config is updated to run the appropriate codemod: - `transform-protect-to-show` for React-based SDKs - `transform-protect-to-show-vue` for Vue/Nuxt SDKs --- .../transform-protect-to-show-vue.fixtures.js | 435 ++++++++++++++++++ .../transform-protect-to-show-vue.test.js | 18 + .../transform-protect-to-show-vue.cjs | 385 ++++++++++++++++ packages/upgrade/src/versions/core-3/index.js | 5 +- 4 files changed, 842 insertions(+), 1 deletion(-) create mode 100644 packages/upgrade/src/codemods/__tests__/__fixtures__/transform-protect-to-show-vue.fixtures.js create mode 100644 packages/upgrade/src/codemods/__tests__/transform-protect-to-show-vue.test.js create mode 100644 packages/upgrade/src/codemods/transform-protect-to-show-vue.cjs diff --git a/packages/upgrade/src/codemods/__tests__/__fixtures__/transform-protect-to-show-vue.fixtures.js b/packages/upgrade/src/codemods/__tests__/__fixtures__/transform-protect-to-show-vue.fixtures.js new file mode 100644 index 00000000000..1dcd0fff575 --- /dev/null +++ b/packages/upgrade/src/codemods/__tests__/__fixtures__/transform-protect-to-show-vue.fixtures.js @@ -0,0 +1,435 @@ +export const fixtures = [ + { + name: 'Transforms Protect import in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms SignedIn in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms SignedOut in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms Protect with permission in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms Protect with feature in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms Protect with plan in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms Protect with multiple auth props in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms Protect with condition in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms Protect with fallback in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms self-closing Protect in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms self-closing SignedIn in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms SignedOut with fallback in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms multiple components in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms ProtectProps type in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms Protect with dynamic binding in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Does not transform non-.vue files', + path: 'component.tsx', + source: `import { Protect } from "@clerk/vue" + +const App = () => ( + + + +)`, + output: null, + }, + { + name: 'Does not transform non-clerk imports in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: null, + }, + { + name: 'Transforms Protect with no props to default signed-in', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, + { + name: 'Transforms Nuxt import in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: null, // #clerk is not @clerk/* so should not transform + }, + { + name: 'Preserves other attributes on Protect in Vue SFC', + path: 'component.vue', + source: ` + +`, + output: ` + +`, + }, +]; diff --git a/packages/upgrade/src/codemods/__tests__/transform-protect-to-show-vue.test.js b/packages/upgrade/src/codemods/__tests__/transform-protect-to-show-vue.test.js new file mode 100644 index 00000000000..2e7591b72db --- /dev/null +++ b/packages/upgrade/src/codemods/__tests__/transform-protect-to-show-vue.test.js @@ -0,0 +1,18 @@ +import { applyTransform } from 'jscodeshift/dist/testUtils'; +import { describe, expect, it } from 'vitest'; + +import transformer from '../transform-protect-to-show-vue.cjs'; +import { fixtures } from './__fixtures__/transform-protect-to-show-vue.fixtures'; + +describe('transform-protect-to-show-vue', () => { + it.each(fixtures)(`$name`, ({ source, output, path }) => { + const result = applyTransform(transformer, {}, { source, path }); + + if (output === null) { + // null output means no transformation should occur + expect(result).toBeFalsy(); + } else { + expect(result).toEqual(output.trim()); + } + }); +}); diff --git a/packages/upgrade/src/codemods/transform-protect-to-show-vue.cjs b/packages/upgrade/src/codemods/transform-protect-to-show-vue.cjs new file mode 100644 index 00000000000..1e31b6646bb --- /dev/null +++ b/packages/upgrade/src/codemods/transform-protect-to-show-vue.cjs @@ -0,0 +1,385 @@ +const CLERK_PACKAGE_PREFIX = '@clerk/'; + +const isClerkPackageSource = sourceValue => { + return typeof sourceValue === 'string' && sourceValue.startsWith(CLERK_PACKAGE_PREFIX); +}; + +/** + * Vue-specific codemod to transform ``, ``, and `` components to ``. + * + * This codemod handles Vue SFC (.vue) files, transforming both: + * - Script imports (ESM/CJS) + * - Template component usage with Vue's v-bind syntax + * + * Template transformations: + * - `` → `` + * - `` → `` + * - `` → `` + * - `` → `` + * - `` → `` + * + * @param {import('jscodeshift').FileInfo} fileInfo - The file information + * @param {import('jscodeshift').API} api - The API object provided by jscodeshift + * @returns {string|undefined} - The transformed source code if modifications were made + */ +module.exports = function transformProtectToShowVue(fileInfo, { jscodeshift: j }) { + const { source, path: filePath } = fileInfo; + + // Only process .vue files + if (!filePath || !filePath.endsWith('.vue')) { + return undefined; + } + + let dirtyFlag = false; + let result = source; + + // Track which components were imported from @clerk/* + const importedComponents = new Set(); + + // Extract and transform + const scriptMatch = result.match(/(]*>)([\s\S]*?)(<\/script\b[^>]*>)/i); if (scriptMatch) { const [fullMatch, openTag, scriptContent, closeTag] = scriptMatch; const scriptResult = transformScript(scriptContent, j); @@ -56,7 +57,8 @@ module.exports = function transformProtectToShowVue(fileInfo, { jscodeshift: j } // Only transform template if we found clerk imports if (importedComponents.size > 0) { // Extract and transform