mirror of
https://github.com/codeflash-ai/codeflash.git
synced 2026-05-04 18:25:17 +00:00
Install cli post cloning in npm
This commit is contained in:
parent
6428a22eba
commit
0b611c722a
2 changed files with 192 additions and 21 deletions
|
|
@ -106,40 +106,63 @@ function installUv() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if git is available
|
||||
*/
|
||||
function hasGit() {
|
||||
try {
|
||||
const result = spawnSync('git', ['--version'], {
|
||||
stdio: 'ignore',
|
||||
shell: true,
|
||||
});
|
||||
return result.status === 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install codeflash Python CLI using uv tool
|
||||
*
|
||||
* Installation priority:
|
||||
* 1. GitHub main branch (if git available) - gets latest features
|
||||
* 2. PyPI (fallback) - stable release
|
||||
*
|
||||
* We prefer GitHub because it has the latest JS/TS support that may not
|
||||
* be published to PyPI yet. uv handles cloning internally in its cache.
|
||||
*/
|
||||
function installCodeflash(uvBin) {
|
||||
logStep('2/3', 'Installing codeflash Python CLI...');
|
||||
|
||||
const GITHUB_REPO = 'git+https://github.com/codeflash-ai/codeflash.git';
|
||||
|
||||
// Priority 1: Install from GitHub (latest features, requires git)
|
||||
if (hasGit()) {
|
||||
try {
|
||||
execSync(`"${uvBin}" tool install --force --python python3.12 "${GITHUB_REPO}"`, {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
});
|
||||
logSuccess('codeflash CLI installed from GitHub (latest)');
|
||||
return true;
|
||||
} catch (error) {
|
||||
logWarning(`GitHub installation failed: ${error.message}`);
|
||||
logWarning('Falling back to PyPI...');
|
||||
}
|
||||
} else {
|
||||
logWarning('Git not found, installing from PyPI...');
|
||||
}
|
||||
|
||||
// Priority 2: Install from PyPI (stable release fallback)
|
||||
try {
|
||||
// Use uv tool install to install codeflash in an isolated environment
|
||||
// This avoids conflicts with any existing Python environments
|
||||
execSync(`"${uvBin}" tool install --force --python python3.12 codeflash`, {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
});
|
||||
logSuccess('codeflash CLI installed successfully');
|
||||
logSuccess('codeflash CLI installed from PyPI');
|
||||
return true;
|
||||
} catch (error) {
|
||||
// If codeflash is not on PyPI yet, try installing from the local package
|
||||
logWarning('codeflash not found on PyPI, trying local installation...');
|
||||
try {
|
||||
// Try installing from the current codeflash repo if we're in development
|
||||
const cliRoot = path.resolve(__dirname, '..', '..', '..');
|
||||
const pyprojectPath = path.join(cliRoot, 'pyproject.toml');
|
||||
|
||||
if (fs.existsSync(pyprojectPath)) {
|
||||
execSync(`"${uvBin}" tool install --force "${cliRoot}"`, {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
});
|
||||
logSuccess('codeflash CLI installed from local source');
|
||||
return true;
|
||||
}
|
||||
} catch (localError) {
|
||||
logError(`Failed to install codeflash: ${localError.message}`);
|
||||
}
|
||||
logError(`Failed to install codeflash: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -545,3 +545,151 @@ function identity<T>(value: T): T {
|
|||
|
||||
assert len(functions) == 1
|
||||
assert functions[0].name == "identity"
|
||||
|
||||
|
||||
class TestExportConstArrowFunctions:
|
||||
"""Tests for export const arrow function pattern - Issue #10.
|
||||
|
||||
Modern TypeScript codebases commonly use:
|
||||
- export const slugify = (str: string) => { return s; }
|
||||
- export const uniqueBy = <T>(array: T[]) => { ... }
|
||||
|
||||
These must be correctly recognized as optimizable functions.
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def ts_analyzer(self):
|
||||
"""Create a TypeScript analyzer."""
|
||||
return TreeSitterAnalyzer(TreeSitterLanguage.TYPESCRIPT)
|
||||
|
||||
def test_export_const_arrow_function_basic(self, ts_analyzer):
|
||||
"""Test finding export const arrow function (basic pattern)."""
|
||||
code = """export const slugify = (str: string) => {
|
||||
return str.toLowerCase();
|
||||
};"""
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
|
||||
assert len(functions) == 1
|
||||
assert functions[0].name == "slugify"
|
||||
assert functions[0].is_arrow is True
|
||||
assert ts_analyzer.has_return_statement(functions[0], code) is True
|
||||
|
||||
def test_export_const_arrow_function_optional_param(self, ts_analyzer):
|
||||
"""Test finding export const arrow function with optional parameter."""
|
||||
code = """export const slugify = (str: string, forDisplayingInput?: boolean) => {
|
||||
if (!str) {
|
||||
return "";
|
||||
}
|
||||
const s = str.toLowerCase();
|
||||
return forDisplayingInput ? s : s.replace(/-+$/, "");
|
||||
};"""
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
|
||||
assert len(functions) == 1
|
||||
assert functions[0].name == "slugify"
|
||||
assert functions[0].is_arrow is True
|
||||
assert ts_analyzer.has_return_statement(functions[0], code) is True
|
||||
|
||||
def test_export_const_generic_arrow_function(self, ts_analyzer):
|
||||
"""Test finding export const arrow function with generics."""
|
||||
code = """export const uniqueBy = <T extends { [key: string]: unknown }>(array: T[], keys: (keyof T)[]) => {
|
||||
return array.filter(
|
||||
(item, index, self) => index === self.findIndex((t) => keys.every((key) => t[key] === item[key]))
|
||||
);
|
||||
};"""
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
|
||||
# Should find uniqueBy, and possibly the inner arrow functions
|
||||
uniqueBy = next((f for f in functions if f.name == "uniqueBy"), None)
|
||||
assert uniqueBy is not None
|
||||
assert uniqueBy.is_arrow is True
|
||||
assert ts_analyzer.has_return_statement(uniqueBy, code) is True
|
||||
|
||||
def test_export_const_arrow_function_is_exported(self, ts_analyzer):
|
||||
"""Test that export const arrow functions are recognized as exported."""
|
||||
code = """export const slugify = (str: string) => {
|
||||
return str.toLowerCase();
|
||||
};"""
|
||||
|
||||
# Check is_function_exported
|
||||
is_exported, export_name = ts_analyzer.is_function_exported(code, "slugify")
|
||||
assert is_exported is True
|
||||
assert export_name == "slugify"
|
||||
|
||||
def test_export_const_with_default_export(self, ts_analyzer):
|
||||
"""Test export const with separate default export."""
|
||||
code = """export const slugify = (str: string) => {
|
||||
return str.toLowerCase();
|
||||
};
|
||||
|
||||
export default slugify;"""
|
||||
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
assert len(functions) == 1
|
||||
assert functions[0].name == "slugify"
|
||||
|
||||
# Should be exported both ways
|
||||
is_named, named_name = ts_analyzer.is_function_exported(code, "slugify")
|
||||
assert is_named is True
|
||||
|
||||
def test_multiple_export_const_functions(self, ts_analyzer):
|
||||
"""Test multiple export const arrow functions in same file."""
|
||||
code = """export const notUndefined = <T>(val: T | undefined): val is T => Boolean(val);
|
||||
|
||||
export const uniqueBy = <T extends { [key: string]: unknown }>(array: T[], keys: (keyof T)[]) => {
|
||||
return array.filter(
|
||||
(item, index, self) => index === self.findIndex((t) => keys.every((key) => t[key] === item[key]))
|
||||
);
|
||||
};"""
|
||||
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
|
||||
# Find the top-level exported functions
|
||||
names = {f.name for f in functions if f.parent_function is None}
|
||||
assert "notUndefined" in names
|
||||
assert "uniqueBy" in names
|
||||
|
||||
def test_export_const_arrow_with_implicit_return(self, ts_analyzer):
|
||||
"""Test export const arrow function with implicit return."""
|
||||
code = """export const double = (n: number) => n * 2;"""
|
||||
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
|
||||
assert len(functions) == 1
|
||||
assert functions[0].name == "double"
|
||||
assert functions[0].is_arrow is True
|
||||
assert ts_analyzer.has_return_statement(functions[0], code) is True
|
||||
|
||||
def test_export_const_async_arrow_function(self, ts_analyzer):
|
||||
"""Test export const async arrow function."""
|
||||
code = """export const fetchData = async (url: string) => {
|
||||
const response = await fetch(url);
|
||||
return response.json();
|
||||
};"""
|
||||
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
|
||||
assert len(functions) == 1
|
||||
assert functions[0].name == "fetchData"
|
||||
assert functions[0].is_arrow is True
|
||||
assert functions[0].is_async is True
|
||||
assert ts_analyzer.has_return_statement(functions[0], code) is True
|
||||
|
||||
def test_non_exported_const_not_exported(self, ts_analyzer):
|
||||
"""Test that non-exported const functions are not marked as exported."""
|
||||
code = """const privateFunc = (x: number) => {
|
||||
return x * 2;
|
||||
};
|
||||
|
||||
export const publicFunc = (x: number) => {
|
||||
return privateFunc(x);
|
||||
};"""
|
||||
|
||||
# privateFunc should not be exported
|
||||
is_private_exported, _ = ts_analyzer.is_function_exported(code, "privateFunc")
|
||||
assert is_private_exported is False
|
||||
|
||||
# publicFunc should be exported
|
||||
is_public_exported, name = ts_analyzer.is_function_exported(code, "publicFunc")
|
||||
assert is_public_exported is True
|
||||
assert name == "publicFunc"
|
||||
|
|
|
|||
Loading…
Reference in a new issue