snapshot of https://github.com/ueberdosis/tiptap.git (main, 2026-09-15; blobs > 131072B omitted)
Some checks failed
Verify / Install node dependencies (push) Failing after 2s
Verify / Build packages (push) Has been skipped
Verify / Check linting & formatting (push) Has been skipped
Verify / Run unit tests (push) Has been skipped
Verify / Run e2e tests (shard 1/4) (push) Has been skipped
Verify / Run e2e tests (shard 2/4) (push) Has been skipped
Verify / Run e2e tests (shard 3/4) (push) Has been skipped
Verify / Run e2e tests (shard 4/4) (push) Has been skipped
Publish / Resolve publish config (push) Failing after 2s
Publish / Send notification to Slack (push) Has been skipped
Publish / Send failure notification to Slack (push) Failing after 8s
Verify / Merge e2e reports (push) Has been skipped
Publish / Build (push) Has been skipped
Publish / Bump Versions OR Release to Registry (push) Has been skipped
Some checks failed
Verify / Install node dependencies (push) Failing after 2s
Verify / Build packages (push) Has been skipped
Verify / Check linting & formatting (push) Has been skipped
Verify / Run unit tests (push) Has been skipped
Verify / Run e2e tests (shard 1/4) (push) Has been skipped
Verify / Run e2e tests (shard 2/4) (push) Has been skipped
Verify / Run e2e tests (shard 3/4) (push) Has been skipped
Verify / Run e2e tests (shard 4/4) (push) Has been skipped
Publish / Resolve publish config (push) Failing after 2s
Publish / Send notification to Slack (push) Has been skipped
Publish / Send failure notification to Slack (push) Failing after 8s
Verify / Merge e2e reports (push) Has been skipped
Publish / Build (push) Has been skipped
Publish / Bump Versions OR Release to Registry (push) Has been skipped
This commit is contained in:
33
codemods/tiptap-v4-migration/scripts/vue-package-renames.ts
Normal file
33
codemods/tiptap-v4-migration/scripts/vue-package-renames.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { Codemod, Edit } from 'codemod:ast-grep'
|
||||
import type JSON from 'codemod:ast-grep/langs/json'
|
||||
import type JS from 'codemod:ast-grep/langs/javascript'
|
||||
import type TS from 'codemod:ast-grep/langs/typescript'
|
||||
import type TSX from 'codemod:ast-grep/langs/tsx'
|
||||
|
||||
const packageSpecifiers = new Map([
|
||||
["'@tiptap/vue-3'", "'@tiptap/vue'"],
|
||||
['"@tiptap/vue-3"', '"@tiptap/vue"'],
|
||||
["'@tiptap/extension-drag-handle-vue-3'", "'@tiptap/extension-drag-handle-vue'"],
|
||||
['"@tiptap/extension-drag-handle-vue-3"', '"@tiptap/extension-drag-handle-vue"'],
|
||||
])
|
||||
|
||||
type Language = JS | TS | TSX | JSON
|
||||
|
||||
const codemod: Codemod<Language> = async root => {
|
||||
const edits: Edit[] = []
|
||||
|
||||
root
|
||||
.root()
|
||||
.findAll({ rule: { kind: 'string' } })
|
||||
.forEach(node => {
|
||||
const replacement = packageSpecifiers.get(node.text())
|
||||
|
||||
if (replacement) {
|
||||
edits.push(node.replace(replacement))
|
||||
}
|
||||
})
|
||||
|
||||
return edits.length > 0 ? root.root().commitEdits(edits) : null
|
||||
}
|
||||
|
||||
export default codemod
|
||||
243
codemods/tiptap-v4-migration/scripts/vue-sfc-imports.mjs
Normal file
243
codemods/tiptap-v4-migration/scripts/vue-sfc-imports.mjs
Normal file
@@ -0,0 +1,243 @@
|
||||
import { readdir, readFile, writeFile } from 'node:fs/promises'
|
||||
import { resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const packageSpecifiers = new Map([
|
||||
['@tiptap/vue-3', '@tiptap/vue'],
|
||||
['@tiptap/extension-drag-handle-vue-3', '@tiptap/extension-drag-handle-vue'],
|
||||
])
|
||||
|
||||
const scriptPattern = /<script\b[^>]*>([\s\S]*?)<\/script>/gi
|
||||
const skippedDirectories = new Set(['.git', 'build', 'dist', 'node_modules'])
|
||||
|
||||
function skipTrivia(source, start) {
|
||||
let index = start
|
||||
|
||||
while (index < source.length) {
|
||||
if (/\s/.test(source[index])) {
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (source.startsWith('//', index)) {
|
||||
const lineEnd = source.indexOf('\n', index + 2)
|
||||
index = lineEnd === -1 ? source.length : lineEnd + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (source.startsWith('/*', index)) {
|
||||
const commentEnd = source.indexOf('*/', index + 2)
|
||||
index = commentEnd === -1 ? source.length : commentEnd + 2
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
return index
|
||||
}
|
||||
|
||||
function readStringEnd(source, start) {
|
||||
const quote = source[start]
|
||||
|
||||
for (let index = start + 1; index < source.length; index += 1) {
|
||||
if (source[index] === '\\') {
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (source[index] === quote) {
|
||||
return index
|
||||
}
|
||||
}
|
||||
|
||||
return source.length
|
||||
}
|
||||
|
||||
function readIdentifierEnd(source, start) {
|
||||
let index = start
|
||||
|
||||
while (index < source.length && /[\w$]/.test(source[index])) {
|
||||
index += 1
|
||||
}
|
||||
|
||||
return index
|
||||
}
|
||||
|
||||
function isIdentifierStart(character) {
|
||||
return /[A-Za-z_$]/.test(character)
|
||||
}
|
||||
|
||||
function getSpecifierEdit(source, start) {
|
||||
const quote = source[start]
|
||||
|
||||
if (quote !== "'" && quote !== '"') {
|
||||
return null
|
||||
}
|
||||
|
||||
const end = readStringEnd(source, start)
|
||||
const specifier = source.slice(start + 1, end)
|
||||
const replacement = packageSpecifiers.get(specifier)
|
||||
|
||||
if (!replacement || end === source.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
end: end + 1,
|
||||
start,
|
||||
text: `${quote}${replacement}${quote}`,
|
||||
}
|
||||
}
|
||||
|
||||
function findSpecifier(source, start, requiresCall) {
|
||||
let index = skipTrivia(source, start)
|
||||
|
||||
if (requiresCall) {
|
||||
if (source[index] !== '(') {
|
||||
return null
|
||||
}
|
||||
|
||||
index = skipTrivia(source, index + 1)
|
||||
}
|
||||
|
||||
return getSpecifierEdit(source, index)
|
||||
}
|
||||
|
||||
function findImportedSpecifier(source, start, limit = source.length) {
|
||||
const end = limit === -1 ? source.length : limit
|
||||
|
||||
for (let index = start; index < end; index += 1) {
|
||||
if (source.startsWith('//', index)) {
|
||||
const lineEnd = source.indexOf('\n', index + 2)
|
||||
index = lineEnd === -1 ? source.length : lineEnd
|
||||
continue
|
||||
}
|
||||
|
||||
if (source.startsWith('/*', index)) {
|
||||
const commentEnd = source.indexOf('*/', index + 2)
|
||||
index = commentEnd === -1 ? source.length : commentEnd + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (source[index] === "'" || source[index] === '"' || source[index] === '`') {
|
||||
index = readStringEnd(source, index)
|
||||
continue
|
||||
}
|
||||
|
||||
if (isIdentifierStart(source[index])) {
|
||||
const end = readIdentifierEnd(source, index)
|
||||
|
||||
if (source.slice(index, end) === 'from') {
|
||||
return findSpecifier(source, end, false)
|
||||
}
|
||||
|
||||
index = end - 1
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function replacePackageSpecifiers(source) {
|
||||
const edits = []
|
||||
|
||||
for (let index = 0; index < source.length;) {
|
||||
if (source.startsWith('//', index)) {
|
||||
const lineEnd = source.indexOf('\n', index + 2)
|
||||
index = lineEnd === -1 ? source.length : lineEnd + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (source.startsWith('/*', index)) {
|
||||
const commentEnd = source.indexOf('*/', index + 2)
|
||||
index = commentEnd === -1 ? source.length : commentEnd + 2
|
||||
continue
|
||||
}
|
||||
|
||||
if (source[index] === "'" || source[index] === '"' || source[index] === '`') {
|
||||
index = readStringEnd(source, index) + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (!isIdentifierStart(source[index])) {
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
|
||||
const end = readIdentifierEnd(source, index)
|
||||
const identifier = source.slice(index, end)
|
||||
const previous = source.slice(0, index).trimEnd().at(-1)
|
||||
|
||||
if (previous === '.') {
|
||||
index = end
|
||||
continue
|
||||
}
|
||||
|
||||
const edit =
|
||||
identifier === 'import'
|
||||
? (findSpecifier(source, end, source[skipTrivia(source, end)] === '(') ??
|
||||
findImportedSpecifier(source, end))
|
||||
: identifier === 'export'
|
||||
? findImportedSpecifier(source, end, source.indexOf('\n', end))
|
||||
: identifier === 'require'
|
||||
? findSpecifier(source, end, true)
|
||||
: null
|
||||
|
||||
if (edit) {
|
||||
edits.push(edit)
|
||||
}
|
||||
|
||||
index = end
|
||||
}
|
||||
|
||||
return edits.reduceRight(
|
||||
(result, edit) => `${result.slice(0, edit.start)}${edit.text}${result.slice(edit.end)}`,
|
||||
source,
|
||||
)
|
||||
}
|
||||
|
||||
/** Rewrites known Tiptap package specifiers inside Vue script blocks. */
|
||||
export function transformVueSource(source) {
|
||||
return source.replace(scriptPattern, (script, content) =>
|
||||
script.replace(content, replacePackageSpecifiers(content)),
|
||||
)
|
||||
}
|
||||
|
||||
async function findVueFiles(directory) {
|
||||
const entries = await readdir(directory, { withFileTypes: true })
|
||||
const files = []
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
if (!skippedDirectories.has(entry.name)) {
|
||||
files.push(...(await findVueFiles(resolve(directory, entry.name))))
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if (entry.isFile() && entry.name.endsWith('.vue')) {
|
||||
files.push(resolve(directory, entry.name))
|
||||
}
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
async function migrateVueFiles(directory) {
|
||||
const files = await findVueFiles(directory)
|
||||
|
||||
for (const file of files) {
|
||||
const source = await readFile(file, 'utf8')
|
||||
const transformedSource = transformVueSource(source)
|
||||
|
||||
if (transformedSource !== source) {
|
||||
await writeFile(file, transformedSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||
await migrateVueFiles(resolve(process.argv[2] ?? process.cwd()))
|
||||
}
|
||||
Reference in New Issue
Block a user