mirror of
https://github.com/langgenius/dify.git
synced 2026-04-15 04:52:38 +00:00
Compare commits
1 Commits
4-14-noUnc
...
quiet-when
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c67d7c8e55 |
15
.github/workflows/pyrefly-diff-comment.yml
vendored
15
.github/workflows/pyrefly-diff-comment.yml
vendored
@@ -79,10 +79,11 @@ jobs:
|
||||
const body = diff.trim()
|
||||
? '### Pyrefly Diff\n<details>\n<summary>base → PR</summary>\n\n```diff\n' + diff + '\n```\n</details>'
|
||||
: '### Pyrefly Diff\nNo changes detected.';
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: prNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body,
|
||||
});
|
||||
if (diff.trim()) {
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: prNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const entryFile = path.join(packageRoot, 'dist', 'cli.mjs')
|
||||
|
||||
if (!fs.existsSync(entryFile))
|
||||
throw new Error(`Built CLI entry not found at ${entryFile}. Run "pnpm --filter @dify/cli build" first.`)
|
||||
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[entryFile, ...process.argv.slice(2)],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
env: process.env,
|
||||
stdio: 'inherit',
|
||||
},
|
||||
)
|
||||
|
||||
if (result.error)
|
||||
throw result.error
|
||||
|
||||
process.exit(result.status ?? 1)
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "@dify/cli",
|
||||
"private": true,
|
||||
"version": "0.0.0-private",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"dify-cli": "./bin/dify-cli.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "vp pack"
|
||||
},
|
||||
"dependencies": {
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vite-plus": "catalog:"
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import process from 'node:process'
|
||||
import { runMigrationCommand } from './no-unchecked-indexed-access/migrate'
|
||||
import { runNormalizeCommand } from './no-unchecked-indexed-access/normalize'
|
||||
import { runBatchMigrationCommand } from './no-unchecked-indexed-access/run'
|
||||
|
||||
type CommandHandler = (argv: string[]) => Promise<void>
|
||||
|
||||
const COMMANDS = new Map<string, CommandHandler>([
|
||||
['migrate', runMigrationCommand],
|
||||
['normalize', runNormalizeCommand],
|
||||
['run', runBatchMigrationCommand],
|
||||
])
|
||||
|
||||
function printUsage() {
|
||||
console.log(`Usage:
|
||||
dify-cli no-unchecked-indexed-access migrate [options]
|
||||
dify-cli no-unchecked-indexed-access run [options]
|
||||
dify-cli no-unchecked-indexed-access normalize`)
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [group, command, ...restArgs] = process.argv.slice(2)
|
||||
|
||||
if (!group || group === 'help' || group === '--help' || group === '-h') {
|
||||
printUsage()
|
||||
return
|
||||
}
|
||||
|
||||
if (group !== 'no-unchecked-indexed-access') {
|
||||
printUsage()
|
||||
throw new Error(`Unknown command group: ${group}`)
|
||||
}
|
||||
|
||||
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
||||
printUsage()
|
||||
return
|
||||
}
|
||||
|
||||
const handler = COMMANDS.get(command)
|
||||
if (!handler) {
|
||||
printUsage()
|
||||
throw new Error(`Unknown command: ${command}`)
|
||||
}
|
||||
|
||||
await handler(restArgs)
|
||||
}
|
||||
|
||||
try {
|
||||
await main()
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error instanceof Error ? error.message : error)
|
||||
process.exitCode = 1
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,51 +0,0 @@
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { normalizeMalformedAssertions } from './migrate'
|
||||
|
||||
const ROOT = process.cwd()
|
||||
const EXTENSIONS = new Set(['.ts', '.tsx'])
|
||||
|
||||
async function collectFiles(directory: string): Promise<string[]> {
|
||||
const entries = await fs.readdir(directory, { withFileTypes: true })
|
||||
const files: string[] = []
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.name === 'node_modules' || entry.name === '.next')
|
||||
continue
|
||||
|
||||
const absolutePath = path.join(directory, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
files.push(...await collectFiles(absolutePath))
|
||||
continue
|
||||
}
|
||||
|
||||
if (!EXTENSIONS.has(path.extname(entry.name)))
|
||||
continue
|
||||
|
||||
files.push(absolutePath)
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const files = await collectFiles(ROOT)
|
||||
let changedFileCount = 0
|
||||
|
||||
await Promise.all(files.map(async (fileName) => {
|
||||
const currentText = await fs.readFile(fileName, 'utf8')
|
||||
const nextText = normalizeMalformedAssertions(currentText)
|
||||
if (nextText === currentText)
|
||||
return
|
||||
|
||||
await fs.writeFile(fileName, nextText)
|
||||
changedFileCount += 1
|
||||
}))
|
||||
|
||||
console.log(`Normalized malformed assertion syntax in ${changedFileCount} file(s).`)
|
||||
}
|
||||
|
||||
export async function runNormalizeCommand(_argv: string[]) {
|
||||
await main()
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
import { execFile } from 'node:child_process'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { promisify } from 'node:util'
|
||||
import { runMigration, SUPPORTED_DIAGNOSTIC_CODES } from './migrate'
|
||||
|
||||
const execFileAsync = promisify(execFile)
|
||||
const DIAGNOSTIC_PATTERN = /^(.+?\.(?:ts|tsx))\((\d+),(\d+)\): error TS(\d+): (.+)$/
|
||||
const DEFAULT_BATCH_SIZE = 100
|
||||
const DEFAULT_BATCH_ITERATIONS = 5
|
||||
const DEFAULT_MAX_ROUNDS = 20
|
||||
|
||||
type CliOptions = {
|
||||
batchIterations: number
|
||||
batchSize: number
|
||||
maxRounds: number
|
||||
project: string
|
||||
verbose: boolean
|
||||
}
|
||||
|
||||
type DiagnosticEntry = {
|
||||
code: number
|
||||
fileName: string
|
||||
line: number
|
||||
message: string
|
||||
}
|
||||
|
||||
function parseArgs(argv: string[]): CliOptions {
|
||||
const options: CliOptions = {
|
||||
batchIterations: DEFAULT_BATCH_ITERATIONS,
|
||||
batchSize: DEFAULT_BATCH_SIZE,
|
||||
maxRounds: DEFAULT_MAX_ROUNDS,
|
||||
project: 'tsconfig.json',
|
||||
verbose: false,
|
||||
}
|
||||
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const arg = argv[i]
|
||||
|
||||
if (arg === '--')
|
||||
continue
|
||||
|
||||
if (arg === '--verbose') {
|
||||
options.verbose = true
|
||||
continue
|
||||
}
|
||||
|
||||
if (arg === '--project') {
|
||||
const value = argv[i + 1]
|
||||
if (!value)
|
||||
throw new Error('Missing value for --project')
|
||||
|
||||
options.project = value
|
||||
i += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (arg === '--batch-size') {
|
||||
const value = Number(argv[i + 1])
|
||||
if (!Number.isInteger(value) || value <= 0)
|
||||
throw new Error('Invalid value for --batch-size')
|
||||
|
||||
options.batchSize = value
|
||||
i += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (arg === '--batch-iterations') {
|
||||
const value = Number(argv[i + 1])
|
||||
if (!Number.isInteger(value) || value <= 0)
|
||||
throw new Error('Invalid value for --batch-iterations')
|
||||
|
||||
options.batchIterations = value
|
||||
i += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (arg === '--max-rounds') {
|
||||
const value = Number(argv[i + 1])
|
||||
if (!Number.isInteger(value) || value <= 0)
|
||||
throw new Error('Invalid value for --max-rounds')
|
||||
|
||||
options.maxRounds = value
|
||||
i += 1
|
||||
continue
|
||||
}
|
||||
|
||||
throw new Error(`Unknown option: ${arg}`)
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
async function runTypeCheck(project: string): Promise<{ diagnostics: DiagnosticEntry[], exitCode: number, rawOutput: string }> {
|
||||
const projectDirectory = path.dirname(path.resolve(process.cwd(), project))
|
||||
|
||||
try {
|
||||
const { stdout, stderr } = await execFileAsync('pnpm', ['exec', 'tsc', '--noEmit', '--pretty', 'false', '--incremental', 'false', '--project', project], {
|
||||
cwd: projectDirectory,
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_OPTIONS: process.env.NODE_OPTIONS ?? '--max-old-space-size=8192',
|
||||
},
|
||||
maxBuffer: 1024 * 1024 * 32,
|
||||
})
|
||||
|
||||
const rawOutput = `${stdout}${stderr}`.trim()
|
||||
return {
|
||||
diagnostics: parseDiagnostics(rawOutput, projectDirectory),
|
||||
exitCode: 0,
|
||||
rawOutput,
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
const exitCode = typeof error === 'object' && error && 'code' in error && typeof error.code === 'number'
|
||||
? error.code
|
||||
: 1
|
||||
const stdout = typeof error === 'object' && error && 'stdout' in error && typeof error.stdout === 'string'
|
||||
? error.stdout
|
||||
: ''
|
||||
const stderr = typeof error === 'object' && error && 'stderr' in error && typeof error.stderr === 'string'
|
||||
? error.stderr
|
||||
: ''
|
||||
const rawOutput = `${stdout}${stderr}`.trim()
|
||||
|
||||
return {
|
||||
diagnostics: parseDiagnostics(rawOutput, projectDirectory),
|
||||
exitCode,
|
||||
rawOutput,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseDiagnostics(rawOutput: string, projectDirectory: string): DiagnosticEntry[] {
|
||||
return rawOutput
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
.flatMap((line) => {
|
||||
const match = line.match(DIAGNOSTIC_PATTERN)
|
||||
if (!match)
|
||||
return []
|
||||
|
||||
return [{
|
||||
code: Number(match[4]),
|
||||
fileName: path.resolve(projectDirectory, match[1]!),
|
||||
line: Number(match[2]),
|
||||
message: match[5] ?? '',
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
function summarizeCodes(diagnostics: DiagnosticEntry[]): string {
|
||||
const counts = new Map<number, number>()
|
||||
for (const diagnostic of diagnostics)
|
||||
counts.set(diagnostic.code, (counts.get(diagnostic.code) ?? 0) + 1)
|
||||
|
||||
return Array.from(counts.entries())
|
||||
.sort((left, right) => right[1] - left[1])
|
||||
.slice(0, 8)
|
||||
.map(([code, count]) => `TS${code}:${count}`)
|
||||
.join(', ')
|
||||
}
|
||||
|
||||
function chunk<T>(items: T[], size: number): T[][] {
|
||||
const batches: T[][] = []
|
||||
for (let i = 0; i < items.length; i += size)
|
||||
batches.push(items.slice(i, i + size))
|
||||
|
||||
return batches
|
||||
}
|
||||
|
||||
async function runBatchMigration(options: CliOptions) {
|
||||
for (let round = 1; round <= options.maxRounds; round += 1) {
|
||||
const { diagnostics, exitCode, rawOutput } = await runTypeCheck(options.project)
|
||||
if (exitCode === 0) {
|
||||
console.log(`Type check passed after ${round - 1} migration round(s).`)
|
||||
return
|
||||
}
|
||||
|
||||
const supportedDiagnostics = diagnostics.filter(diagnostic => SUPPORTED_DIAGNOSTIC_CODES.has(diagnostic.code))
|
||||
const unsupportedDiagnostics = diagnostics.filter(diagnostic => !SUPPORTED_DIAGNOSTIC_CODES.has(diagnostic.code))
|
||||
const supportedFiles = Array.from(new Set(supportedDiagnostics.map(diagnostic => diagnostic.fileName)))
|
||||
|
||||
console.log(`Round ${round}: ${diagnostics.length} diagnostic(s). ${summarizeCodes(diagnostics)}`)
|
||||
|
||||
if (options.verbose) {
|
||||
for (const diagnostic of diagnostics.slice(0, 40))
|
||||
console.log(`${path.relative(process.cwd(), diagnostic.fileName)}:${diagnostic.line} TS${diagnostic.code} ${diagnostic.message}`)
|
||||
}
|
||||
|
||||
if (supportedFiles.length === 0) {
|
||||
console.error('No supported diagnostics remain to migrate.')
|
||||
if (unsupportedDiagnostics.length > 0) {
|
||||
console.error('Remaining unsupported diagnostics:')
|
||||
for (const diagnostic of unsupportedDiagnostics.slice(0, 40))
|
||||
console.error(`${path.relative(process.cwd(), diagnostic.fileName)}:${diagnostic.line} TS${diagnostic.code} ${diagnostic.message}`)
|
||||
}
|
||||
if (rawOutput)
|
||||
process.stderr.write(`${rawOutput}\n`)
|
||||
process.exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
let roundEdits = 0
|
||||
const batches = chunk(supportedFiles, options.batchSize)
|
||||
|
||||
for (const [index, batch] of batches.entries()) {
|
||||
console.log(` Batch ${index + 1}/${batches.length}: ${batch.length} file(s)`)
|
||||
const result = await runMigration({
|
||||
files: batch,
|
||||
maxIterations: options.batchIterations,
|
||||
project: options.project,
|
||||
verbose: options.verbose,
|
||||
write: true,
|
||||
})
|
||||
roundEdits += result.totalEdits
|
||||
}
|
||||
|
||||
if (roundEdits === 0) {
|
||||
console.error('Migration script made no edits in this round; stopping to avoid an infinite loop.')
|
||||
process.exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
console.error(`Reached --max-rounds=${options.maxRounds} before type check passed.`)
|
||||
process.exitCode = 1
|
||||
}
|
||||
|
||||
export async function runBatchMigrationCommand(argv: string[]) {
|
||||
await runBatchMigration(parseArgs(argv))
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { defineConfig } from 'vite-plus'
|
||||
|
||||
export default defineConfig({
|
||||
pack: {
|
||||
clean: true,
|
||||
deps: {
|
||||
neverBundle: ['typescript'],
|
||||
},
|
||||
entry: ['src/cli.ts'],
|
||||
format: ['esm'],
|
||||
outDir: 'dist',
|
||||
platform: 'node',
|
||||
sourcemap: true,
|
||||
target: 'node22',
|
||||
treeshake: true,
|
||||
},
|
||||
})
|
||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@@ -599,22 +599,6 @@ importers:
|
||||
specifier: 'catalog:'
|
||||
version: 0.1.16(@types/node@25.6.0)(@voidzero-dev/vite-plus-core@0.1.16(@types/node@25.6.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(happy-dom@20.9.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3)
|
||||
|
||||
packages/cli:
|
||||
dependencies:
|
||||
typescript:
|
||||
specifier: 'catalog:'
|
||||
version: 6.0.2
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: 'catalog:'
|
||||
version: 25.6.0
|
||||
vite:
|
||||
specifier: npm:@voidzero-dev/vite-plus-core@0.1.16
|
||||
version: '@voidzero-dev/vite-plus-core@0.1.16(@types/node@25.6.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3)'
|
||||
vite-plus:
|
||||
specifier: 'catalog:'
|
||||
version: 0.1.16(@types/node@25.6.0)(@voidzero-dev/vite-plus-core@0.1.16(@types/node@25.6.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(happy-dom@20.9.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3)
|
||||
|
||||
packages/iconify-collections:
|
||||
devDependencies:
|
||||
iconify-import-svg:
|
||||
@@ -969,9 +953,6 @@ importers:
|
||||
'@chromatic-com/storybook':
|
||||
specifier: 'catalog:'
|
||||
version: 5.1.2(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))
|
||||
'@dify/cli':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/cli
|
||||
'@dify/iconify-collections':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/iconify-collections
|
||||
|
||||
@@ -17,8 +17,9 @@ const config: KnipConfig = {
|
||||
],
|
||||
ignoreDependencies: [
|
||||
'@iconify-json/*',
|
||||
|
||||
'@storybook/addon-onboarding',
|
||||
'@dify/cli',
|
||||
|
||||
],
|
||||
/// keep-sorted
|
||||
rules: {
|
||||
|
||||
@@ -159,7 +159,6 @@
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "catalog:",
|
||||
"@chromatic-com/storybook": "catalog:",
|
||||
"@dify/cli": "workspace:*",
|
||||
"@dify/iconify-collections": "workspace:*",
|
||||
"@egoist/tailwindcss-icons": "catalog:",
|
||||
"@eslint-react/eslint-plugin": "catalog:",
|
||||
|
||||
Reference in New Issue
Block a user