Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
91 changes: 91 additions & 0 deletions packages/taro-cli/src/__tests__/project.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import Project from '../create/project'

jest.mock('@tarojs/helper', () => ({
chalk: {
blueBright: jest.fn((text) => text),
green: jest.fn((text) => text),
red: jest.fn((text) => text)
},
DEFAULT_TEMPLATE_SRC: 'https://github.com/NervJS/taro-project-templates',
DEFAULT_TEMPLATE_SRC_GITEE: 'https://gitee.com/o2team/taro-project-templates',
fs: {
ensureDirSync: jest.fn(),
existsSync: jest.fn(() => true)
},
getUserHomeDir: jest.fn(() => '/tmp'),
SOURCE_DIR: 'src',
TARO_BASE_CONFIG: 'index.json',
TARO_CONFIG_FOLDER: '.taro'
}))

jest.mock('@tarojs/shared', () => ({
isArray: Array.isArray
}), { virtual: true })

jest.mock('@tarojs/binding', () => ({
CompilerType: {
Solid: 'solid',
Vite: 'vite',
Webpack5: 'webpack5'
},
createProject: jest.fn(() => Promise.resolve()),
CSSType: {
None: 'none'
},
FrameworkType: {
Preact: 'preact',
React: 'react',
Solid: 'solid',
Vue3: 'vue3'
},
NpmType: {
Npm: 'npm'
},
PeriodType: {
CreateAPP: 'createApp'
}
}))

describe('create project', () => {
let logSpy: jest.SpyInstance

beforeEach(() => {
logSpy = jest.spyOn(console, 'log').mockImplementation()
})

afterEach(() => {
logSpy.mockRestore()
})

it('should resolve dot project name to current directory', () => {
const project = new Project({
projectDir: '/a/b/current-project',
projectName: '.',
sourceRoot: __dirname,
template: 'default',
templateSource: 'default-template',
npm: 'npm',
css: 'none',
framework: 'react'
} as any)

expect(project.conf.projectDir).toBe('/a/b')
expect(project.conf.projectName).toBe('current-project')
})

it('should resolve current directory path project name to current directory', () => {
const project = new Project({
projectDir: '/a/b/current-project',
projectName: './',
sourceRoot: __dirname,
template: 'default',
templateSource: 'default-template',
npm: 'npm',
css: 'none',
framework: 'react'
} as any)

expect(project.conf.projectDir).toBe('/a/b')
expect(project.conf.projectName).toBe('current-project')
})
})
15 changes: 13 additions & 2 deletions packages/taro-cli/src/create/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ interface AskMethods {

const NONE_AVAILABLE_TEMPLATE = '无可用模板'

function resolveProjectConf (conf: IProjectConfOptions): IProjectConfOptions {
if (!conf.projectName || !/^\.(?:[\\/])?$/.test(conf.projectName)) return conf

const projectDir = conf.projectDir || process.cwd()
return {
...conf,
projectDir: path.dirname(projectDir),
projectName: path.basename(projectDir)
}
}
Comment on lines +59 to +68

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 描述:检查 askProjectName 方法的实现,确认其存在性检查逻辑

# 查找 askProjectName 方法的完整实现,包括上下文
rg -nP -A 20 'askProjectName.*function' --type=ts

# 查找 create() 和 ask() 方法的调用链,确认 cwd 上下文
rg -nP -B 5 -A 10 'async (ask|create)\s*\(' packages/taro-cli/src/create/project.ts

Repository: NervJS/taro

Length of output: 2553


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="packages/taro-cli/src/create/project.ts"

echo "---- file stats ----"
wc -l "$file"

echo "---- resolveProjectConf + constructor integration (lines 1-140) ----"
sed -n '1,220p' "$file" | nl -ba | sed -n '1,220p'

echo "---- askProjectName full block (lines around 130-220) ----"
sed -n '130,230p' "$file" | nl -ba

echo "---- look for resolveProjectConf usage ----"
rg -n "resolveProjectConf" "$file"

Repository: NervJS/taro

Length of output: 250


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="packages/taro-cli/src/create/project.ts"

echo "---- file stats ----"
wc -l "$file"

echo "---- resolveProjectConf + constructor integration (lines 1-140) ----"
sed -n '1,220p' "$file" | nl -ba | sed -n '1,220p'

echo "---- askProjectName full block (lines around 130-220) ----"
sed -n '130,230p' "$file" | nl -ba

echo "---- look for resolveProjectConf usage ----"
rg -n "resolveProjectConf" "$file"

Repository: NervJS/taro

Length of output: 250


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="packages/taro-cli/src/create/project.ts"

echo "---- resolveProjectConf definition ----"
rg -n "function resolveProjectConf" "$file"
sed -n '40,110p' "$file" | cat -n

echo "---- constructor / call site around integration ----"
rg -n "resolveProjectConf" "$file"
# show region around matches
rg -n "resolveProjectConf" "$file" | cut -d: -f1 | head -n 5 | while read -r ln; do
  start=$((ln-40)); end=$((ln+40));
  echo "---- context around line $ln ----"
  sed -n "${start},${end}p" "$file" | cat -n
done

echo "---- askProjectName block (160-210) ----"
sed -n '140,220p' "$file" | cat -n

Repository: NervJS/taro

Length of output: 11182


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="packages/taro-cli/src/create/project.ts"

echo "---- usages of projectDir/projectName in this file ----"
rg -n "projectDir|projectName" "$file" 

echo "---- locate write() in file ----"
rg -n "async write|write\s*\(" "$file"

echo "---- print surrounding of write usage ----"
# show region around 'write(' call inside create/creator if any
rg -n "this\.write" "$file" -n

Repository: NervJS/taro

Length of output: 1100


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="packages/taro-cli/src/create/project.ts"

echo "---- write() implementation around 460-520 ----"
sed -n '440,520p' "$file" | cat -n

echo "---- around where projectRoot/projectName are derived (470-510) ----"
sed -n '470,510p' "$file" | cat -n

Repository: NervJS/taro

Length of output: 5065


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "new Project\\(" -S packages/taro-cli/src | head -n 50
rg -n "projectName:\\s*\\w+|projectName\\s*:" packages/taro-cli/src/create -S | head -n 50

Repository: NervJS/taro

Length of output: 890


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "new Project\\(" packages/taro-cli/src -S | head -n 50
rg -n "projectName" packages/taro-cli/src/init packages/taro-cli/src -S | head -n 80

Repository: NervJS/taro

Length of output: 3572


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# 1) locate createProject implementation and how it uses projectRoot/projectName
rg -n "function createProject\\b|createProject\\s*\\(" packages/taro-cli/src/create -S
rg -n "createProject\\s*\\(" packages/taro-cli/src -S

# 2) inspect init command construction of Project
sed -n '1,140p' packages/taro-cli/src/presets/commands/init.ts | cat -n

# 3) inspect cli argument parsing around init [projectName]
rg -n "init \\[projectName\\]|case 'init'|program\\.command\\('init'\\)|taro init" packages/taro-cli/src/cli.ts -S
sed -n '150,270p' packages/taro-cli/src/cli.ts | cat -n

# 4) inspect existing unit tests for projectName '.' conversion
sed -n '1,140p' packages/taro-cli/src/__tests__/project.spec.ts | cat -n

Repository: NervJS/taro

Length of output: 9312


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "appPath" packages/taro-cli/src -S | head -n 200

Repository: NervJS/taro

Length of output: 7327


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# inspect cli.ts to see how appPath is computed for init command
sed -n '1,140p' packages/taro-cli/src/cli.ts | cat -n
rg -n "class .*Kernel|customCommand\\(|new Kernel|paths\\b|this\\.paths|ctx\\.paths" packages/taro-cli/src/cli.ts -S
# inspect utils/run* helper to see how appPath is passed in tests/exec
sed -n '1,120p' packages/taro-cli/src/__tests__/utils/index.ts | cat -n

Repository: NervJS/taro

Length of output: 8133


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# inspect cli.ts to see how appPath is computed for init command
sed -n '1,140p' packages/taro-cli/src/cli.ts | cat -n
rg -n "customCommand\\(|paths\\b|this\\.paths|ctx\\.paths" packages/taro-cli/src/cli.ts -S
# inspect utils/run* helper to see how appPath is passed in tests/exec
sed -n '1,120p' packages/taro-cli/src/__tests__/utils/index.ts | cat -n

Repository: NervJS/taro

Length of output: 8096


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "createProject\\b" packages/taro-cli/src -S
rg -n "function createProject\\b|export (async )?function createProject\\b|export const createProject\\b" . -S

Repository: NervJS/taro

Length of output: 544


修正 taro init .askProjectName 存在性校验的路径不一致问题

resolveProjectConfprojectName././ 时会把配置改成 projectDir: path.dirname(cwd)projectName: path.basename(cwd)packages/taro-cli/src/create/project.ts 第 59-68 行)。但 askProjectName 的存在性校验在 projectName 已是字符串时直接做 fs.existsSync(conf.projectName!)(第 158-190 行),这会导致 taro init . 时实际检查的是 <cwd>/<basename>(例如 /a/b/current-project/current-project),而目标目录其实是 <cwd>。只有当该“嵌套目录”恰好存在时才会误报“当前目录已经存在同名项目”。

建议让校验依据与实际创建目标路径一致:改为检查 fs.existsSync(path.join(conf.projectDir, conf.projectName)),或在 projectName 输入为 ././ 时跳过/使用匹配目标目录的验证逻辑。现有 packages/taro-cli/src/__tests__/project.spec.ts 只覆盖 ././ 转换结果,未覆盖 askProjectName 的校验行为。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/taro-cli/src/create/project.ts` around lines 59 - 68, The existence
check in askProjectName is inconsistent with resolveProjectConf for inputs like
"." or "./" — resolveProjectConf rewrites projectDir/projectName based on cwd
but askProjectName currently calls fs.existsSync(conf.projectName!), which
effectively checks the wrong nested path; update askProjectName to test the
actual target directory by using fs.existsSync(path.join(conf.projectDir,
conf.projectName)) (or special-case conf.projectName === '.'/'./' to check
conf.projectDir) so the existence validation matches the resolved creation
target used by resolveProjectConf.


export default class Project extends Creator {
public rootPath: string
public conf: IProjectConfOptions
Expand All @@ -68,7 +79,7 @@ export default class Project extends Creator {
}
this.rootPath = this._rootPath

this.conf = Object.assign(
this.conf = resolveProjectConf(Object.assign(
{
projectName: '',
projectDir: '',
Expand All @@ -77,7 +88,7 @@ export default class Project extends Creator {
npm: ''
},
options
)
))
}

init () {
Expand Down