prd-to-flutter 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -0
- package/bin/p2f.mjs +18 -0
- package/dist/cli.js +203 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/clean.js +35 -0
- package/dist/commands/clean.js.map +1 -0
- package/dist/commands/doctor.js +148 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/generate.js +120 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.js +46 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/paths.js +58 -0
- package/dist/commands/paths.js.map +1 -0
- package/dist/commands/remove.js +23 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/screenshots-audit.js +39 -0
- package/dist/commands/screenshots-audit.js.map +1 -0
- package/dist/commands/skills-install.js +21 -0
- package/dist/commands/skills-install.js.map +1 -0
- package/dist/commands/sync.js +84 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/update.js +93 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/core/existing-page-detector.js +463 -0
- package/dist/core/existing-page-detector.js.map +1 -0
- package/dist/core/fail-fast.js +50 -0
- package/dist/core/fail-fast.js.map +1 -0
- package/dist/core/feature-coverage-builder.js +667 -0
- package/dist/core/feature-coverage-builder.js.map +1 -0
- package/dist/core/flutter-project-scanner.js +393 -0
- package/dist/core/flutter-project-scanner.js.map +1 -0
- package/dist/core/implementation-plan-writer.js +190 -0
- package/dist/core/implementation-plan-writer.js.map +1 -0
- package/dist/core/logger.js +39 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/package-info.js +33 -0
- package/dist/core/package-info.js.map +1 -0
- package/dist/core/paths.js +37 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/playwright-capture.js +539 -0
- package/dist/core/playwright-capture.js.map +1 -0
- package/dist/core/playwright-cli.js +26 -0
- package/dist/core/playwright-cli.js.map +1 -0
- package/dist/core/playwright-install.js +40 -0
- package/dist/core/playwright-install.js.map +1 -0
- package/dist/core/prd-clone.js +131 -0
- package/dist/core/prd-clone.js.map +1 -0
- package/dist/core/prd-install.js +108 -0
- package/dist/core/prd-install.js.map +1 -0
- package/dist/core/profile.js +149 -0
- package/dist/core/profile.js.map +1 -0
- package/dist/core/project-doc-reader.js +252 -0
- package/dist/core/project-doc-reader.js.map +1 -0
- package/dist/core/report-writer.js +90 -0
- package/dist/core/report-writer.js.map +1 -0
- package/dist/core/route-name.js +60 -0
- package/dist/core/route-name.js.map +1 -0
- package/dist/core/run-context.js +160 -0
- package/dist/core/run-context.js.map +1 -0
- package/dist/core/screenshot-auditor.js +405 -0
- package/dist/core/screenshot-auditor.js.map +1 -0
- package/dist/core/screenshot-exploration-plan-writer.js +200 -0
- package/dist/core/screenshot-exploration-plan-writer.js.map +1 -0
- package/dist/core/semantic-model-builder.js +922 -0
- package/dist/core/semantic-model-builder.js.map +1 -0
- package/dist/core/skill-install.js +78 -0
- package/dist/core/skill-install.js.map +1 -0
- package/dist/core/stage-stub.js +24 -0
- package/dist/core/stage-stub.js.map +1 -0
- package/dist/core/task-index-writer.js +149 -0
- package/dist/core/task-index-writer.js.map +1 -0
- package/dist/core/update-checker.js +155 -0
- package/dist/core/update-checker.js.map +1 -0
- package/dist/core/vue-page-locator.js +748 -0
- package/dist/core/vue-page-locator.js.map +1 -0
- package/dist/core/vue-project-reader.js +116 -0
- package/dist/core/vue-project-reader.js.map +1 -0
- package/docs/artifacts-and-agent.md +203 -0
- package/docs/development.md +118 -0
- package/docs/usage.md +246 -0
- package/package.json +50 -0
- package/skills/p2f/SKILL.md +303 -0
- package/skills/p2f/references/page-layout-patterns.md +120 -0
- package/skills/p2f/references/youfi-flutter-guidelines.md +71 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import { failFast } from './fail-fast.js';
|
|
5
|
+
import { logger } from './logger.js';
|
|
6
|
+
import { PROTOTYPE_BRANCH, PROTOTYPE_REMOTE } from './paths.js';
|
|
7
|
+
import { markComplete } from './run-context.js';
|
|
8
|
+
// 网络异常时 SSH 默认可能长时间 hang。用较短的 ConnectTimeout 快速失败。
|
|
9
|
+
// 允许外部通过 GIT_SSH_COMMAND 覆盖。
|
|
10
|
+
if (!process.env.GIT_SSH_COMMAND) {
|
|
11
|
+
process.env.GIT_SSH_COMMAND = 'ssh -o ConnectTimeout=5 -o BatchMode=yes';
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Fetch `origin` and report how local HEAD relates to `origin/<branch>`.
|
|
15
|
+
* Throws on git failure — callers decide whether to surface or downgrade
|
|
16
|
+
* (doctor downgrades to a 'miss' status; sync fails-fast).
|
|
17
|
+
*/
|
|
18
|
+
export async function checkPrdFreshness(prototypeDir) {
|
|
19
|
+
await execa('git', ['-C', prototypeDir, 'fetch', 'origin', PROTOTYPE_BRANCH], {
|
|
20
|
+
stdout: 'pipe',
|
|
21
|
+
stderr: 'pipe',
|
|
22
|
+
});
|
|
23
|
+
const localCommit = await readHeadCommit(prototypeDir);
|
|
24
|
+
const remoteCommit = (await execa('git', ['-C', prototypeDir, 'rev-parse', `origin/${PROTOTYPE_BRANCH}`], {
|
|
25
|
+
stdout: 'pipe',
|
|
26
|
+
})).stdout.trim();
|
|
27
|
+
if (localCommit === remoteCommit) {
|
|
28
|
+
return { upToDate: true, localCommit, remoteCommit, behind: 0, ahead: 0 };
|
|
29
|
+
}
|
|
30
|
+
const behind = Number((await execa('git', ['-C', prototypeDir, 'rev-list', '--count', `HEAD..origin/${PROTOTYPE_BRANCH}`], { stdout: 'pipe' })).stdout.trim());
|
|
31
|
+
const ahead = Number((await execa('git', ['-C', prototypeDir, 'rev-list', '--count', `origin/${PROTOTYPE_BRANCH}..HEAD`], { stdout: 'pipe' })).stdout.trim());
|
|
32
|
+
return { upToDate: false, localCommit, remoteCommit, behind, ahead };
|
|
33
|
+
}
|
|
34
|
+
export async function prdClone(ctx) {
|
|
35
|
+
const prototypeDir = ctx.paths.prdSourceDir;
|
|
36
|
+
mkdirSync(dirname(prototypeDir), { recursive: true });
|
|
37
|
+
if (!existsSync(prototypeDir)) {
|
|
38
|
+
const result = await initialClone(ctx, prototypeDir);
|
|
39
|
+
ctx.prototypeCommit = result.commit;
|
|
40
|
+
ctx.prototypeSource = result.source;
|
|
41
|
+
markComplete(ctx, 'sync-prd-source');
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
const result = await updateExisting(ctx, prototypeDir);
|
|
45
|
+
ctx.prototypeCommit = result.commit;
|
|
46
|
+
ctx.prototypeSource = result.source;
|
|
47
|
+
markComplete(ctx, 'sync-prd-source');
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
async function initialClone(ctx, prototypeDir) {
|
|
51
|
+
try {
|
|
52
|
+
logger.info(`正在从 PRD 远端克隆到 ${prototypeDir} · ${PROTOTYPE_REMOTE}#${PROTOTYPE_BRANCH}`);
|
|
53
|
+
await execa('git', ['clone', '-b', PROTOTYPE_BRANCH, PROTOTYPE_REMOTE, prototypeDir], {
|
|
54
|
+
stdout: 'pipe',
|
|
55
|
+
stderr: 'pipe',
|
|
56
|
+
});
|
|
57
|
+
const commit = await readHeadCommit(prototypeDir);
|
|
58
|
+
logger.success(`PRD 源码已克隆到本地副本 · ${commit.slice(0, 7)}`);
|
|
59
|
+
return { path: prototypeDir, commit, source: 'remote', branch: PROTOTYPE_BRANCH };
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
failFast({
|
|
63
|
+
stage: 'sync-prd-source',
|
|
64
|
+
action: `git clone -b ${PROTOTYPE_BRANCH} ${PROTOTYPE_REMOTE}`,
|
|
65
|
+
reason: '原型仓库远端 clone 失败:' + (err.shortMessage ?? err.message),
|
|
66
|
+
suggestions: [
|
|
67
|
+
'检查网络是否可以访问 code.nexita.net。',
|
|
68
|
+
'确认当前账号有 TradeAppPrd 仓库 SSH 权限。',
|
|
69
|
+
'确认 git@code.nexita.net:inno/youfi/web/TradeAppPrd.git 可访问。',
|
|
70
|
+
],
|
|
71
|
+
needsUserDecision: ['请解决网络或仓库权限后重试。'],
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function updateExisting(ctx, prototypeDir) {
|
|
76
|
+
try {
|
|
77
|
+
await ensureRemote(prototypeDir, 'origin', PROTOTYPE_REMOTE);
|
|
78
|
+
await execa('git', ['-C', prototypeDir, 'fetch', 'origin', PROTOTYPE_BRANCH], {
|
|
79
|
+
stdout: 'pipe',
|
|
80
|
+
stderr: 'pipe',
|
|
81
|
+
});
|
|
82
|
+
await hardResetTo(prototypeDir, `origin/${PROTOTYPE_BRANCH}`);
|
|
83
|
+
const commit = await readHeadCommit(prototypeDir);
|
|
84
|
+
logger.success(`PRD 本地副本已重置到 origin/${PROTOTYPE_BRANCH} · ${commit.slice(0, 7)}`);
|
|
85
|
+
return {
|
|
86
|
+
path: prototypeDir,
|
|
87
|
+
commit,
|
|
88
|
+
source: 'remote',
|
|
89
|
+
branch: PROTOTYPE_BRANCH,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
failFast({
|
|
94
|
+
stage: 'sync-prd-source',
|
|
95
|
+
action: `git fetch origin ${PROTOTYPE_BRANCH} && git reset --hard origin/${PROTOTYPE_BRANCH}`,
|
|
96
|
+
reason: '从 PRD 远端拉取并重置本地副本失败:' + (err.shortMessage ?? err.message),
|
|
97
|
+
suggestions: [
|
|
98
|
+
'检查网络是否可以访问 code.nexita.net。',
|
|
99
|
+
'确认当前账号有 TradeAppPrd 仓库 SSH 权限。',
|
|
100
|
+
'确认 .p2f-workspace/prd-source 没有被人工改坏;必要时删除后重试。',
|
|
101
|
+
],
|
|
102
|
+
needsUserDecision: ['请解决网络、仓库权限或本地目录问题后重试。'],
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function ensureRemote(prototypeDir, name, url) {
|
|
107
|
+
const { stdout } = await execa('git', ['-C', prototypeDir, 'remote'], { stdout: 'pipe' });
|
|
108
|
+
const names = stdout.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
|
|
109
|
+
if (names.includes(name)) {
|
|
110
|
+
await execa('git', ['-C', prototypeDir, 'remote', 'set-url', name, url]);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
await execa('git', ['-C', prototypeDir, 'remote', 'add', name, url]);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function hardResetTo(prototypeDir, ref) {
|
|
117
|
+
await execa('git', ['-C', prototypeDir, 'checkout', '-f', PROTOTYPE_BRANCH], {
|
|
118
|
+
stdout: 'pipe',
|
|
119
|
+
stderr: 'pipe',
|
|
120
|
+
}).catch(async () => {
|
|
121
|
+
// Branch may not exist locally yet; create tracking branch from ref.
|
|
122
|
+
await execa('git', ['-C', prototypeDir, 'checkout', '-B', PROTOTYPE_BRANCH, ref]);
|
|
123
|
+
});
|
|
124
|
+
await execa('git', ['-C', prototypeDir, 'reset', '--hard', ref]);
|
|
125
|
+
await execa('git', ['-C', prototypeDir, 'clean', '-fd']);
|
|
126
|
+
}
|
|
127
|
+
async function readHeadCommit(prototypeDir) {
|
|
128
|
+
const { stdout } = await execa('git', ['-C', prototypeDir, 'rev-parse', 'HEAD']);
|
|
129
|
+
return stdout.trim();
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=prd-clone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd-clone.js","sourceRoot":"","sources":["../../src/core/prd-clone.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { existsSync, statSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import { failFast } from './fail-fast.js';
|
|
5
|
+
import { logger } from './logger.js';
|
|
6
|
+
import { markComplete } from './run-context.js';
|
|
7
|
+
/**
|
|
8
|
+
* Install JS dependencies for the cloned prototype project so users can run its
|
|
9
|
+
* dev server (`npm run dev`) immediately after `init` without an extra step.
|
|
10
|
+
*
|
|
11
|
+
* Idempotent: skips when `node_modules` is newer than `package.json`, mirroring
|
|
12
|
+
* the heuristic previously used by the p2f shell workflow.
|
|
13
|
+
*/
|
|
14
|
+
export async function prdInstall(ctx, prototypeRoot) {
|
|
15
|
+
const sub = locatePrototypeSub(prototypeRoot);
|
|
16
|
+
const pkgPath = join(sub, 'package.json');
|
|
17
|
+
if (!existsSync(pkgPath)) {
|
|
18
|
+
failFast({
|
|
19
|
+
stage: 'prd-install',
|
|
20
|
+
action: `read ${pkgPath}`,
|
|
21
|
+
reason: '原型仓库 package.json 未找到,无法安装依赖。',
|
|
22
|
+
completedSteps: [...ctx.completedSteps],
|
|
23
|
+
pendingSteps: [...ctx.pendingSteps],
|
|
24
|
+
suggestions: [
|
|
25
|
+
'检查原型仓库结构是否变更(prototype/、frontend/、web/ 子目录)。',
|
|
26
|
+
'确认 TradeAppPrd 是否仍是 Vue 子工程。',
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const pm = await detectPackageManager();
|
|
31
|
+
if (!shouldInstall(sub)) {
|
|
32
|
+
logger.info(`Prototype deps already up-to-date · ${sub} (pm=${pm})`);
|
|
33
|
+
markComplete(ctx, 'prd-install');
|
|
34
|
+
return { path: sub, packageManager: pm, ran: false };
|
|
35
|
+
}
|
|
36
|
+
logger.info(`Installing prototype deps via ${pm} install · ${sub}`);
|
|
37
|
+
try {
|
|
38
|
+
await execa(pm, ['install'], {
|
|
39
|
+
cwd: sub,
|
|
40
|
+
stdout: 'inherit',
|
|
41
|
+
stderr: 'inherit',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
const msg = err.shortMessage ?? err.message;
|
|
46
|
+
failFast({
|
|
47
|
+
stage: 'prd-install',
|
|
48
|
+
action: `${pm} install (cwd=${sub})`,
|
|
49
|
+
reason: `原型项目依赖安装失败:${msg}`,
|
|
50
|
+
completedSteps: [...ctx.completedSteps],
|
|
51
|
+
pendingSteps: [...ctx.pendingSteps],
|
|
52
|
+
suggestions: [
|
|
53
|
+
`进入 ${sub} 手工执行 \`${pm} install\` 复现报错。`,
|
|
54
|
+
'检查 npm registry / 公司代理是否可用。',
|
|
55
|
+
'若 lockfile 与本地 Node 版本不兼容,可删除 node_modules 后重试。',
|
|
56
|
+
],
|
|
57
|
+
needsUserDecision: ['请解决依赖源或权限问题后重新运行 init。'],
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
logger.success(`Prototype deps installed · ${sub}`);
|
|
61
|
+
markComplete(ctx, 'prd-install');
|
|
62
|
+
return { path: sub, packageManager: pm, ran: true };
|
|
63
|
+
}
|
|
64
|
+
function locatePrototypeSub(root) {
|
|
65
|
+
const candidates = [
|
|
66
|
+
join(root, 'prototype'),
|
|
67
|
+
join(root, 'frontend'),
|
|
68
|
+
join(root, 'web'),
|
|
69
|
+
root,
|
|
70
|
+
];
|
|
71
|
+
for (const c of candidates) {
|
|
72
|
+
if (existsSync(join(c, 'package.json')) && existsSync(join(c, 'src'))) {
|
|
73
|
+
return c;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return root;
|
|
77
|
+
}
|
|
78
|
+
async function detectPackageManager() {
|
|
79
|
+
if (await hasBinary('pnpm'))
|
|
80
|
+
return 'pnpm';
|
|
81
|
+
if (await hasBinary('yarn'))
|
|
82
|
+
return 'yarn';
|
|
83
|
+
return 'npm';
|
|
84
|
+
}
|
|
85
|
+
async function hasBinary(name) {
|
|
86
|
+
try {
|
|
87
|
+
await execa(name, ['--version'], { stdout: 'pipe', stderr: 'pipe' });
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function shouldInstall(sub) {
|
|
95
|
+
const nm = join(sub, 'node_modules');
|
|
96
|
+
if (!existsSync(nm))
|
|
97
|
+
return true;
|
|
98
|
+
const pkg = join(sub, 'package.json');
|
|
99
|
+
try {
|
|
100
|
+
const pkgMtime = statSync(pkg).mtimeMs;
|
|
101
|
+
const nmMtime = statSync(nm).mtimeMs;
|
|
102
|
+
return pkgMtime > nmMtime;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=prd-install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd-install.js","sourceRoot":"","sources":["../../src/core/prd-install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, join } from 'node:path';
|
|
3
|
+
export class ProfileConfigError extends Error {
|
|
4
|
+
name = 'ProfileConfigError';
|
|
5
|
+
}
|
|
6
|
+
export const DEFAULT_PROFILE_NAME = 'youfi-default';
|
|
7
|
+
export const DEFAULT_PROJECT_PROFILE = {
|
|
8
|
+
name: DEFAULT_PROFILE_NAME,
|
|
9
|
+
flutter: {
|
|
10
|
+
appRootCandidates: ['lib/app'],
|
|
11
|
+
moduleRoots: ['lib/app/modules'],
|
|
12
|
+
moduleViewDirs: ['views'],
|
|
13
|
+
moduleWidgetDirs: ['widgets'],
|
|
14
|
+
moduleControllerDirs: ['controllers'],
|
|
15
|
+
pageFileSuffixes: ['page', 'screen', 'view'],
|
|
16
|
+
routeFiles: {
|
|
17
|
+
parser: 'getx',
|
|
18
|
+
constants: [
|
|
19
|
+
'lib/app/routes/utils/app_routes.dart',
|
|
20
|
+
'lib/app/routes/app_routes.dart',
|
|
21
|
+
],
|
|
22
|
+
pages: [
|
|
23
|
+
'lib/app/routes/utils/app_pages.dart',
|
|
24
|
+
'lib/app/routes/app_pages.dart',
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
commonWidgetRoots: [
|
|
28
|
+
'lib/app/common/widget',
|
|
29
|
+
'lib/app/common/widgets',
|
|
30
|
+
'lib/app/widgets',
|
|
31
|
+
],
|
|
32
|
+
themeTokenFiles: {
|
|
33
|
+
appColors: ['lib/app/common/theme/app_colors.dart'],
|
|
34
|
+
semanticColors: ['lib/app/common/theme/app_colors_extension.dart'],
|
|
35
|
+
textStyles: ['lib/app/common/theme/text_style_extension.dart'],
|
|
36
|
+
},
|
|
37
|
+
assetRoots: ['assets'],
|
|
38
|
+
translationRoots: ['lib/app/translations'],
|
|
39
|
+
},
|
|
40
|
+
prototype: {
|
|
41
|
+
subprojectCandidates: ['prototype', 'frontend', 'web', '.'],
|
|
42
|
+
routerDirs: ['src/router', 'src/routes'],
|
|
43
|
+
themeDirs: ['src/theme', 'src/themes'],
|
|
44
|
+
stylesDirs: ['src/styles', 'src/style', 'src/assets/styles'],
|
|
45
|
+
componentsDirs: ['src/components'],
|
|
46
|
+
viewsDirs: ['src/views', 'src/pages'],
|
|
47
|
+
registryFiles: [
|
|
48
|
+
{ path: 'src/config/specScreens.js', kind: 'spec' },
|
|
49
|
+
{ path: 'src/config/prdPageRegistry.js', kind: 'prototype' },
|
|
50
|
+
{ path: 'src/config/designScreens.js', kind: 'design' },
|
|
51
|
+
],
|
|
52
|
+
notesRoots: ['notes'],
|
|
53
|
+
i18nPrototypeRoots: ['src/i18n/prototype'],
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
const BUILTIN_PROFILES = {
|
|
57
|
+
[DEFAULT_PROFILE_NAME]: DEFAULT_PROJECT_PROFILE,
|
|
58
|
+
};
|
|
59
|
+
const CONFIG_CANDIDATES = ['p2f.config.json', '.p2f/config.json'];
|
|
60
|
+
export function loadProjectProfile(root) {
|
|
61
|
+
const found = findConfig(root);
|
|
62
|
+
if (!found) {
|
|
63
|
+
return { profile: cloneProfile(DEFAULT_PROJECT_PROFILE), configPath: null };
|
|
64
|
+
}
|
|
65
|
+
let config;
|
|
66
|
+
try {
|
|
67
|
+
config = JSON.parse(readFileSync(found, 'utf8'));
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
throw new ProfileConfigError(`读取 p2f profile 配置失败:${found} · ${err.message}`);
|
|
71
|
+
}
|
|
72
|
+
const baseName = config.profile ?? DEFAULT_PROFILE_NAME;
|
|
73
|
+
const base = BUILTIN_PROFILES[baseName];
|
|
74
|
+
if (!base) {
|
|
75
|
+
throw new ProfileConfigError(`未知 p2f profile:${baseName}。当前内置 profile:${Object.keys(BUILTIN_PROFILES).join(', ')}`);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
profile: mergeProjectProfile(base, config),
|
|
79
|
+
configPath: found,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function resolveProfilePath(root, path) {
|
|
83
|
+
return isAbsolute(path) ? path : join(root, path);
|
|
84
|
+
}
|
|
85
|
+
export function pickExistingProfilePath(root, paths) {
|
|
86
|
+
for (const configured of paths) {
|
|
87
|
+
const absolute = resolveProfilePath(root, configured);
|
|
88
|
+
if (existsSync(absolute))
|
|
89
|
+
return { absolute, configured };
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
function findConfig(root) {
|
|
94
|
+
for (const rel of CONFIG_CANDIDATES) {
|
|
95
|
+
const path = join(root, rel);
|
|
96
|
+
if (existsSync(path))
|
|
97
|
+
return path;
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
function mergeProjectProfile(base, config) {
|
|
102
|
+
const flutter = config.flutter ?? {};
|
|
103
|
+
const routeFiles = flutter.routeFiles ?? {};
|
|
104
|
+
const themeTokenFiles = flutter.themeTokenFiles ?? {};
|
|
105
|
+
const prototype = config.prototype ?? {};
|
|
106
|
+
const routeParser = routeFiles.parser ?? base.flutter.routeFiles.parser;
|
|
107
|
+
if (routeParser !== 'getx') {
|
|
108
|
+
throw new ProfileConfigError(`当前 CLI 暂不支持 flutter.routeFiles.parser=${String(routeParser)},请升级 CLI 或新增 route parser adapter。`);
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
name: config.profile ?? base.name,
|
|
112
|
+
flutter: {
|
|
113
|
+
appRootCandidates: flutter.appRootCandidates ?? base.flutter.appRootCandidates,
|
|
114
|
+
moduleRoots: flutter.moduleRoots ?? base.flutter.moduleRoots,
|
|
115
|
+
moduleViewDirs: flutter.moduleViewDirs ?? base.flutter.moduleViewDirs,
|
|
116
|
+
moduleWidgetDirs: flutter.moduleWidgetDirs ?? base.flutter.moduleWidgetDirs,
|
|
117
|
+
moduleControllerDirs: flutter.moduleControllerDirs ?? base.flutter.moduleControllerDirs,
|
|
118
|
+
pageFileSuffixes: flutter.pageFileSuffixes ?? base.flutter.pageFileSuffixes,
|
|
119
|
+
routeFiles: {
|
|
120
|
+
parser: routeParser,
|
|
121
|
+
constants: routeFiles.constants ?? base.flutter.routeFiles.constants,
|
|
122
|
+
pages: routeFiles.pages ?? base.flutter.routeFiles.pages,
|
|
123
|
+
},
|
|
124
|
+
commonWidgetRoots: flutter.commonWidgetRoots ?? base.flutter.commonWidgetRoots,
|
|
125
|
+
themeTokenFiles: {
|
|
126
|
+
appColors: themeTokenFiles.appColors ?? base.flutter.themeTokenFiles.appColors,
|
|
127
|
+
semanticColors: themeTokenFiles.semanticColors ?? base.flutter.themeTokenFiles.semanticColors,
|
|
128
|
+
textStyles: themeTokenFiles.textStyles ?? base.flutter.themeTokenFiles.textStyles,
|
|
129
|
+
},
|
|
130
|
+
assetRoots: flutter.assetRoots ?? base.flutter.assetRoots,
|
|
131
|
+
translationRoots: flutter.translationRoots ?? base.flutter.translationRoots,
|
|
132
|
+
},
|
|
133
|
+
prototype: {
|
|
134
|
+
subprojectCandidates: prototype.subprojectCandidates ?? base.prototype.subprojectCandidates,
|
|
135
|
+
routerDirs: prototype.routerDirs ?? base.prototype.routerDirs,
|
|
136
|
+
themeDirs: prototype.themeDirs ?? base.prototype.themeDirs,
|
|
137
|
+
stylesDirs: prototype.stylesDirs ?? base.prototype.stylesDirs,
|
|
138
|
+
componentsDirs: prototype.componentsDirs ?? base.prototype.componentsDirs,
|
|
139
|
+
viewsDirs: prototype.viewsDirs ?? base.prototype.viewsDirs,
|
|
140
|
+
registryFiles: prototype.registryFiles ?? base.prototype.registryFiles,
|
|
141
|
+
notesRoots: prototype.notesRoots ?? base.prototype.notesRoots,
|
|
142
|
+
i18nPrototypeRoots: prototype.i18nPrototypeRoots ?? base.prototype.i18nPrototypeRoots,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function cloneProfile(profile) {
|
|
147
|
+
return JSON.parse(JSON.stringify(profile));
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/core/profile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
3
|
+
import { logger } from './logger.js';
|
|
4
|
+
import { markComplete } from './run-context.js';
|
|
5
|
+
import { failFast } from './fail-fast.js';
|
|
6
|
+
import { pickExistingProfilePath, resolveProfilePath, } from './profile.js';
|
|
7
|
+
const MAX_EXCERPT = 6_000;
|
|
8
|
+
export function readFlutterProjectDocs(ctx, profile, profileConfigPath) {
|
|
9
|
+
const root = ctx.paths.root;
|
|
10
|
+
if (!existsSync(join(root, 'pubspec.yaml'))) {
|
|
11
|
+
failFast({
|
|
12
|
+
stage: 'flutter-doc-read',
|
|
13
|
+
action: 'locate pubspec.yaml',
|
|
14
|
+
reason: '工作目录未找到 pubspec.yaml,无法确认为 Flutter 项目根目录。',
|
|
15
|
+
completedSteps: [...ctx.completedSteps],
|
|
16
|
+
pendingSteps: [...ctx.pendingSteps],
|
|
17
|
+
suggestions: [
|
|
18
|
+
'在 Flutter 项目根目录下执行 CLI。',
|
|
19
|
+
'或显式传入 --workspace <path>。',
|
|
20
|
+
],
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const readmeExcerpt = readExcerpt(join(root, 'README.md'));
|
|
24
|
+
const claudeMdExcerpt = readExcerpt(join(root, 'CLAUDE.md'));
|
|
25
|
+
const agentsMdExcerpt = readExcerpt(join(root, 'AGENTS.md'));
|
|
26
|
+
const pubspecSummary = readPubspec(join(root, 'pubspec.yaml'));
|
|
27
|
+
const assetsDirs = scanAssets(root, profile);
|
|
28
|
+
const modulesIndex = scanModules(root, profile);
|
|
29
|
+
const translationsIndex = scanTranslations(root, profile);
|
|
30
|
+
const pageSuffix = inferPageSuffix(root, modulesIndex, profile);
|
|
31
|
+
const routeConstants = pickExistingProfilePath(root, profile.flutter.routeFiles.constants);
|
|
32
|
+
const routePages = pickExistingProfilePath(root, profile.flutter.routeFiles.pages);
|
|
33
|
+
const outDir = join(ctx.pageDir, 'analysis');
|
|
34
|
+
mkdirSync(outDir, { recursive: true });
|
|
35
|
+
const writtenTo = join(outDir, 'project-rules.md');
|
|
36
|
+
const md = [
|
|
37
|
+
`# Flutter App 项目规则摘要`,
|
|
38
|
+
'',
|
|
39
|
+
`- workspaceRoot: \`${root}\``,
|
|
40
|
+
`- p2f profile: \`${profile.name}\`${profileConfigPath ? `(${profileConfigPath})` : '(builtin)'}`,
|
|
41
|
+
`- pubspec.name: \`${pubspecSummary.name}\``,
|
|
42
|
+
`- pubspec.version: \`${pubspecSummary.version ?? '未知'}\``,
|
|
43
|
+
`- 页面后缀: \`${pageSuffix}\``,
|
|
44
|
+
`- route parser: \`${profile.flutter.routeFiles.parser}\``,
|
|
45
|
+
`- route pages: \`${routePages?.configured ?? profile.flutter.routeFiles.pages[0] ?? '(未配置)'}\``,
|
|
46
|
+
`- route constants: \`${routeConstants?.configured ?? profile.flutter.routeFiles.constants[0] ?? '(未配置)'}\``,
|
|
47
|
+
`- module roots: ${profile.flutter.moduleRoots.map((p) => `\`${p}\``).join(', ') || '(无)'}`,
|
|
48
|
+
'',
|
|
49
|
+
'## pubspec.yaml assets 已声明',
|
|
50
|
+
pubspecSummary.flutterAssetsDeclared.length
|
|
51
|
+
? pubspecSummary.flutterAssetsDeclared.map((a) => `- ${a}`).join('\n')
|
|
52
|
+
: '- (无)',
|
|
53
|
+
'',
|
|
54
|
+
'## assets 目录',
|
|
55
|
+
assetsDirs.length ? assetsDirs.map((a) => `- ${a}`).join('\n') : '- (无)',
|
|
56
|
+
'',
|
|
57
|
+
'## 模块目录列表',
|
|
58
|
+
modulesIndex.length
|
|
59
|
+
? modulesIndex.map((m) => `- ${m}`).join('\n')
|
|
60
|
+
: '- (无)',
|
|
61
|
+
'',
|
|
62
|
+
'## 翻译文件',
|
|
63
|
+
translationsIndex.length
|
|
64
|
+
? translationsIndex.map((t) => `- ${t}`).join('\n')
|
|
65
|
+
: '- (无)',
|
|
66
|
+
'',
|
|
67
|
+
'## README.md 摘要',
|
|
68
|
+
'',
|
|
69
|
+
'```markdown',
|
|
70
|
+
readmeExcerpt || '(缺失)',
|
|
71
|
+
'```',
|
|
72
|
+
'',
|
|
73
|
+
'## CLAUDE.md 摘要',
|
|
74
|
+
'',
|
|
75
|
+
'```markdown',
|
|
76
|
+
claudeMdExcerpt || '(missing)',
|
|
77
|
+
'```',
|
|
78
|
+
'',
|
|
79
|
+
'## AGENTS.md 摘要',
|
|
80
|
+
'',
|
|
81
|
+
'```markdown',
|
|
82
|
+
agentsMdExcerpt || '(missing)',
|
|
83
|
+
'```',
|
|
84
|
+
'',
|
|
85
|
+
].join('\n');
|
|
86
|
+
writeFileSync(writtenTo, md, 'utf8');
|
|
87
|
+
logger.success(`Flutter 项目摘要 → ${writtenTo}`);
|
|
88
|
+
markComplete(ctx, 'read-flutter-docs');
|
|
89
|
+
return {
|
|
90
|
+
profileName: profile.name,
|
|
91
|
+
profileConfigPath,
|
|
92
|
+
readmeExcerpt,
|
|
93
|
+
claudeMdExcerpt,
|
|
94
|
+
agentsMdExcerpt,
|
|
95
|
+
pubspecSummary,
|
|
96
|
+
assetsDirs,
|
|
97
|
+
modulesIndex,
|
|
98
|
+
translationsIndex,
|
|
99
|
+
pageSuffix,
|
|
100
|
+
writtenTo,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function readExcerpt(path) {
|
|
104
|
+
if (!existsSync(path))
|
|
105
|
+
return '';
|
|
106
|
+
const body = readFileSync(path, 'utf8');
|
|
107
|
+
return body.length > MAX_EXCERPT ? body.slice(0, MAX_EXCERPT) + '\n…(truncated)' : body;
|
|
108
|
+
}
|
|
109
|
+
function readPubspec(path) {
|
|
110
|
+
const body = readFileSync(path, 'utf8');
|
|
111
|
+
const name = /^name:\s*(\S+)/m.exec(body)?.[1] ?? 'unknown';
|
|
112
|
+
const version = /^version:\s*(\S+)/m.exec(body)?.[1];
|
|
113
|
+
const flutterAssetsDeclared = extractAssetsBlock(body);
|
|
114
|
+
return { name, version, flutterAssetsDeclared };
|
|
115
|
+
}
|
|
116
|
+
function extractAssetsBlock(pubspecBody) {
|
|
117
|
+
// Naive but adequate extractor for a `flutter: assets:` yaml section.
|
|
118
|
+
const lines = pubspecBody.split(/\r?\n/);
|
|
119
|
+
const out = [];
|
|
120
|
+
let inFlutter = false;
|
|
121
|
+
let inAssets = false;
|
|
122
|
+
for (const raw of lines) {
|
|
123
|
+
if (/^flutter:\s*$/.test(raw)) {
|
|
124
|
+
inFlutter = true;
|
|
125
|
+
inAssets = false;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (inFlutter && /^\S/.test(raw) && !/^flutter:/.test(raw)) {
|
|
129
|
+
inFlutter = false;
|
|
130
|
+
inAssets = false;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (inFlutter && /^\s{2}assets:\s*$/.test(raw)) {
|
|
134
|
+
inAssets = true;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (inAssets) {
|
|
138
|
+
const match = /^\s{4,}-\s+(.+?)\s*$/.exec(raw);
|
|
139
|
+
if (match?.[1]) {
|
|
140
|
+
out.push(match[1]);
|
|
141
|
+
}
|
|
142
|
+
else if (/^\s{0,2}\S/.test(raw)) {
|
|
143
|
+
inAssets = false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
function scanAssets(root, profile) {
|
|
150
|
+
const entries = [];
|
|
151
|
+
for (const baseRel of profile.flutter.assetRoots) {
|
|
152
|
+
const base = resolveProfilePath(root, baseRel);
|
|
153
|
+
if (!existsSync(base))
|
|
154
|
+
continue;
|
|
155
|
+
walk(base, (p, isDir) => {
|
|
156
|
+
if (isDir)
|
|
157
|
+
entries.push(toProjectPath(root, p) + '/');
|
|
158
|
+
}, 2);
|
|
159
|
+
}
|
|
160
|
+
return entries.sort();
|
|
161
|
+
}
|
|
162
|
+
function scanModules(root, profile) {
|
|
163
|
+
const out = [];
|
|
164
|
+
for (const baseRel of profile.flutter.moduleRoots) {
|
|
165
|
+
const base = resolveProfilePath(root, baseRel);
|
|
166
|
+
if (!existsSync(base))
|
|
167
|
+
continue;
|
|
168
|
+
walk(base, (p, isDir) => {
|
|
169
|
+
if (!isDir)
|
|
170
|
+
return;
|
|
171
|
+
if (p === base)
|
|
172
|
+
return;
|
|
173
|
+
const depth = relative(base, p).split('/').length;
|
|
174
|
+
if (depth === 1)
|
|
175
|
+
out.push(toProjectPath(root, p));
|
|
176
|
+
}, 1);
|
|
177
|
+
}
|
|
178
|
+
return out.sort();
|
|
179
|
+
}
|
|
180
|
+
function scanTranslations(root, profile) {
|
|
181
|
+
const out = [];
|
|
182
|
+
for (const baseRel of profile.flutter.translationRoots) {
|
|
183
|
+
const base = resolveProfilePath(root, baseRel);
|
|
184
|
+
if (!existsSync(base))
|
|
185
|
+
continue;
|
|
186
|
+
walk(base, (p, isDir) => {
|
|
187
|
+
if (!isDir && p.endsWith('.dart'))
|
|
188
|
+
out.push(toProjectPath(root, p));
|
|
189
|
+
}, 2);
|
|
190
|
+
}
|
|
191
|
+
return out.sort();
|
|
192
|
+
}
|
|
193
|
+
function inferPageSuffix(root, modules, profile) {
|
|
194
|
+
const counts = {};
|
|
195
|
+
const suffixRe = buildPageSuffixRegex(profile.flutter.pageFileSuffixes);
|
|
196
|
+
for (const mod of modules) {
|
|
197
|
+
for (const viewDir of profile.flutter.moduleViewDirs) {
|
|
198
|
+
const viewsDir = resolveProfilePath(root, join(mod, viewDir));
|
|
199
|
+
if (!existsSync(viewsDir))
|
|
200
|
+
continue;
|
|
201
|
+
walk(viewsDir, (p, isDir) => {
|
|
202
|
+
if (isDir || !p.endsWith('.dart'))
|
|
203
|
+
return;
|
|
204
|
+
const file = relative(viewsDir, p);
|
|
205
|
+
const m = suffixRe.exec(file);
|
|
206
|
+
if (m?.[1])
|
|
207
|
+
counts[m[1]] = (counts[m[1]] ?? 0) + 1;
|
|
208
|
+
}, 3);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
let best = profile.flutter.pageFileSuffixes[0] ?? 'page';
|
|
212
|
+
let bestCount = -1;
|
|
213
|
+
for (const [k, v] of Object.entries(counts)) {
|
|
214
|
+
if (v > bestCount) {
|
|
215
|
+
best = k;
|
|
216
|
+
bestCount = v;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return best;
|
|
220
|
+
}
|
|
221
|
+
function buildPageSuffixRegex(suffixes) {
|
|
222
|
+
const pattern = suffixes.length
|
|
223
|
+
? suffixes.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')
|
|
224
|
+
: 'page|screen|view';
|
|
225
|
+
return new RegExp(`_(${pattern})\\.dart$`);
|
|
226
|
+
}
|
|
227
|
+
function toProjectPath(root, path) {
|
|
228
|
+
const rel = relative(root, path);
|
|
229
|
+
return rel && !rel.startsWith('..') ? rel : path;
|
|
230
|
+
}
|
|
231
|
+
function walk(dir, visit, depth) {
|
|
232
|
+
if (depth < 0)
|
|
233
|
+
return;
|
|
234
|
+
let entries;
|
|
235
|
+
try {
|
|
236
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
for (const e of entries) {
|
|
242
|
+
const full = join(dir, e.name);
|
|
243
|
+
if (e.isDirectory()) {
|
|
244
|
+
visit(full, true);
|
|
245
|
+
walk(full, visit, depth - 1);
|
|
246
|
+
}
|
|
247
|
+
else if (e.isFile()) {
|
|
248
|
+
visit(full, false);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=project-doc-reader.js.map
|