@wp-typia/project-tools 0.22.5 → 0.22.7
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/dist/runtime/ai-feature-capability.js +20 -0
- package/dist/runtime/cli-add-block-json.d.ts +2 -2
- package/dist/runtime/cli-add-block-json.js +5 -4
- package/dist/runtime/cli-add-block.js +16 -11
- package/dist/runtime/cli-add-collision.js +213 -136
- package/dist/runtime/cli-add-help.js +1 -1
- package/dist/runtime/cli-add-kind-ids.d.ts +11 -0
- package/dist/runtime/cli-add-kind-ids.js +20 -0
- package/dist/runtime/cli-add-types.d.ts +2 -8
- package/dist/runtime/cli-add-types.js +1 -17
- package/dist/runtime/cli-add-workspace-ability-scaffold.d.ts +3 -1
- package/dist/runtime/cli-add-workspace-ability-scaffold.js +22 -5
- package/dist/runtime/cli-add-workspace-ability.d.ts +4 -0
- package/dist/runtime/cli-add-workspace-ability.js +5 -1
- package/dist/runtime/cli-add-workspace-admin-view-source.d.ts +7 -0
- package/dist/runtime/cli-add-workspace-admin-view-source.js +9 -10
- package/dist/runtime/cli-add-workspace-admin-view-types.d.ts +0 -2
- package/dist/runtime/cli-add-workspace-admin-view-types.js +0 -3
- package/dist/runtime/cli-add-workspace-ai-scaffold.js +14 -6
- package/dist/runtime/cli-add-workspace.js +8 -11
- package/dist/runtime/cli-doctor-workspace-bindings.js +2 -3
- package/dist/runtime/cli-doctor-workspace-blocks.js +2 -3
- package/dist/runtime/cli-doctor-workspace-features.js +6 -11
- package/dist/runtime/cli-doctor-workspace-shared.d.ts +8 -0
- package/dist/runtime/cli-doctor-workspace-shared.js +10 -0
- package/dist/runtime/cli-help.js +1 -1
- package/dist/runtime/cli-init-apply.d.ts +15 -0
- package/dist/runtime/cli-init-apply.js +99 -0
- package/dist/runtime/cli-init-package-json.d.ts +19 -0
- package/dist/runtime/cli-init-package-json.js +191 -0
- package/dist/runtime/cli-init-plan-presentation.d.ts +16 -0
- package/dist/runtime/cli-init-plan-presentation.js +74 -0
- package/dist/runtime/cli-init-plan.d.ts +39 -0
- package/dist/runtime/cli-init-plan.js +303 -0
- package/dist/runtime/cli-init-templates.d.ts +27 -0
- package/dist/runtime/cli-init-templates.js +244 -0
- package/dist/runtime/cli-init-types.d.ts +84 -0
- package/dist/runtime/cli-init-types.js +3 -0
- package/dist/runtime/cli-init.d.ts +4 -100
- package/dist/runtime/cli-init.js +6 -878
- package/dist/runtime/fs-async.d.ts +28 -0
- package/dist/runtime/fs-async.js +53 -0
- package/dist/runtime/package-managers.js +1 -1
- package/dist/runtime/package-versions.d.ts +1 -1
- package/dist/runtime/package-versions.js +1 -1
- package/dist/runtime/php-utils.d.ts +16 -0
- package/dist/runtime/php-utils.js +258 -1
- package/dist/runtime/scaffold-apply-utils.js +10 -20
- package/dist/runtime/scaffold-bootstrap.js +6 -8
- package/dist/runtime/scaffold-compatibility.d.ts +15 -3
- package/dist/runtime/scaffold-compatibility.js +42 -11
- package/dist/runtime/scaffold-documents.js +12 -0
- package/dist/runtime/scaffold-package-manager-files.js +4 -3
- package/dist/runtime/string-case.d.ts +5 -0
- package/dist/runtime/string-case.js +54 -2
- package/dist/runtime/template-source-cache.d.ts +19 -0
- package/dist/runtime/template-source-cache.js +164 -28
- package/dist/runtime/template-source-external.d.ts +7 -0
- package/dist/runtime/template-source-external.js +22 -5
- package/dist/runtime/template-source-normalization.d.ts +1 -1
- package/dist/runtime/template-source-normalization.js +12 -12
- package/dist/runtime/template-source-remote.d.ts +14 -0
- package/dist/runtime/template-source-remote.js +91 -15
- package/dist/runtime/template-source.js +35 -25
- package/dist/runtime/typia-llm.js +7 -0
- package/dist/runtime/version-floor.js +8 -2
- package/dist/runtime/workspace-inventory.d.ts +16 -14
- package/dist/runtime/workspace-inventory.js +58 -14
- package/package.json +6 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return whether a filesystem path exists without blocking the event loop.
|
|
3
|
+
*
|
|
4
|
+
* @param filePath Absolute or relative path to check.
|
|
5
|
+
* @returns `true` when the path can be accessed, otherwise `false`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function pathExists(filePath: string): Promise<boolean>;
|
|
8
|
+
/**
|
|
9
|
+
* Read a UTF-8 file when it exists and otherwise return `null`.
|
|
10
|
+
*
|
|
11
|
+
* @param filePath Absolute or relative file path to read.
|
|
12
|
+
* @returns The UTF-8 source, or `null` when the file is missing.
|
|
13
|
+
*/
|
|
14
|
+
export declare function readOptionalUtf8File(filePath: string): Promise<string | null>;
|
|
15
|
+
/**
|
|
16
|
+
* Extract a Node.js error code from an unknown thrown value.
|
|
17
|
+
*
|
|
18
|
+
* @param error Unknown error value.
|
|
19
|
+
* @returns The string error code, or an empty string when unavailable.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getNodeErrorCode(error: unknown): string;
|
|
22
|
+
/**
|
|
23
|
+
* Return whether an unknown error represents a missing filesystem path.
|
|
24
|
+
*
|
|
25
|
+
* @param error Unknown error value.
|
|
26
|
+
* @returns `true` when the error has Node.js code `ENOENT`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isFileNotFoundError(error: unknown): boolean;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { promises as fsp } from "node:fs";
|
|
2
|
+
/**
|
|
3
|
+
* Return whether a filesystem path exists without blocking the event loop.
|
|
4
|
+
*
|
|
5
|
+
* @param filePath Absolute or relative path to check.
|
|
6
|
+
* @returns `true` when the path can be accessed, otherwise `false`.
|
|
7
|
+
*/
|
|
8
|
+
export async function pathExists(filePath) {
|
|
9
|
+
try {
|
|
10
|
+
await fsp.access(filePath);
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Read a UTF-8 file when it exists and otherwise return `null`.
|
|
19
|
+
*
|
|
20
|
+
* @param filePath Absolute or relative file path to read.
|
|
21
|
+
* @returns The UTF-8 source, or `null` when the file is missing.
|
|
22
|
+
*/
|
|
23
|
+
export async function readOptionalUtf8File(filePath) {
|
|
24
|
+
try {
|
|
25
|
+
return await fsp.readFile(filePath, "utf8");
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (isFileNotFoundError(error)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Extract a Node.js error code from an unknown thrown value.
|
|
36
|
+
*
|
|
37
|
+
* @param error Unknown error value.
|
|
38
|
+
* @returns The string error code, or an empty string when unavailable.
|
|
39
|
+
*/
|
|
40
|
+
export function getNodeErrorCode(error) {
|
|
41
|
+
return typeof error === "object" && error !== null && "code" in error
|
|
42
|
+
? String(error.code)
|
|
43
|
+
: "";
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Return whether an unknown error represents a missing filesystem path.
|
|
47
|
+
*
|
|
48
|
+
* @param error Unknown error value.
|
|
49
|
+
* @returns `true` when the error has Node.js code `ENOENT`.
|
|
50
|
+
*/
|
|
51
|
+
export function isFileNotFoundError(error) {
|
|
52
|
+
return getNodeErrorCode(error) === "ENOENT";
|
|
53
|
+
}
|
|
@@ -23,7 +23,7 @@ export declare const DEFAULT_WORDPRESS_CORE_ABILITIES_VERSION = "^0.9.0";
|
|
|
23
23
|
export declare const DEFAULT_WORDPRESS_CORE_DATA_VERSION = "^7.44.0";
|
|
24
24
|
export declare const DEFAULT_WORDPRESS_DATA_VERSION = "^9.28.0";
|
|
25
25
|
export declare const DEFAULT_WORDPRESS_DATAVIEWS_VERSION = "^14.1.0";
|
|
26
|
-
export declare const DEFAULT_WP_TYPIA_DATAVIEWS_VERSION = "^0.1.
|
|
26
|
+
export declare const DEFAULT_WP_TYPIA_DATAVIEWS_VERSION = "^0.1.1";
|
|
27
27
|
/**
|
|
28
28
|
* Resolve a managed package version range from linked workspace packages first,
|
|
29
29
|
* then installed package manifests, while preserving the shared normalization
|
|
@@ -19,7 +19,7 @@ export const DEFAULT_WORDPRESS_CORE_ABILITIES_VERSION = '^0.9.0';
|
|
|
19
19
|
export const DEFAULT_WORDPRESS_CORE_DATA_VERSION = '^7.44.0';
|
|
20
20
|
export const DEFAULT_WORDPRESS_DATA_VERSION = '^9.28.0';
|
|
21
21
|
export const DEFAULT_WORDPRESS_DATAVIEWS_VERSION = '^14.1.0';
|
|
22
|
-
export const DEFAULT_WP_TYPIA_DATAVIEWS_VERSION = '^0.1.
|
|
22
|
+
export const DEFAULT_WP_TYPIA_DATAVIEWS_VERSION = '^0.1.1';
|
|
23
23
|
let cachedPackageVersions = null;
|
|
24
24
|
function getErrorCode(error) {
|
|
25
25
|
return typeof error === 'object' && error !== null && 'code' in error
|
|
@@ -12,5 +12,21 @@ export type ReplacePhpFunctionDefinitionOptions = PhpFunctionRangeOptions & {
|
|
|
12
12
|
export declare function escapeRegex(value: string): string;
|
|
13
13
|
export declare function quotePhpString(value: string): string;
|
|
14
14
|
export declare function hasPhpFunctionDefinition(source: string, functionName: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Detect a PHP function call outside strings, comments, heredoc, and nowdoc blocks.
|
|
17
|
+
*
|
|
18
|
+
* @param source PHP source to scan.
|
|
19
|
+
* @param functionName Literal PHP function identifier to find.
|
|
20
|
+
* @returns Whether `source` contains a code-mode call to `functionName`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function hasPhpFunctionCall(source: string, functionName: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Locate a PHP function body without counting braces in non-code regions.
|
|
25
|
+
*
|
|
26
|
+
* @param source PHP source to scan.
|
|
27
|
+
* @param functionName Literal PHP function identifier to locate.
|
|
28
|
+
* @param options Range options such as trailing newline inclusion.
|
|
29
|
+
* @returns The matched {@link PhpFunctionRange}, or `null` when no safe range exists.
|
|
30
|
+
*/
|
|
15
31
|
export declare function findPhpFunctionRange(source: string, functionName: string, options?: PhpFunctionRangeOptions): PhpFunctionRange | null;
|
|
16
32
|
export declare function replacePhpFunctionDefinition(source: string, functionName: string, replacement: string, options?: ReplacePhpFunctionDefinitionOptions): string | null;
|
|
@@ -7,6 +7,250 @@ export function quotePhpString(value) {
|
|
|
7
7
|
export function hasPhpFunctionDefinition(source, functionName) {
|
|
8
8
|
return new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\(`, "u").test(source);
|
|
9
9
|
}
|
|
10
|
+
function isPhpIdentifierStart(character) {
|
|
11
|
+
return /^[A-Za-z_]$/u.test(character ?? "");
|
|
12
|
+
}
|
|
13
|
+
function isPhpIdentifierPart(character) {
|
|
14
|
+
return /^[A-Za-z0-9_]$/u.test(character ?? "");
|
|
15
|
+
}
|
|
16
|
+
function isPhpLineStart(source, index) {
|
|
17
|
+
return index === 0 || source[index - 1] === "\n";
|
|
18
|
+
}
|
|
19
|
+
function isPhpHorizontalWhitespace(character) {
|
|
20
|
+
return character === " " || character === "\t";
|
|
21
|
+
}
|
|
22
|
+
function isPhpWhitespace(character) {
|
|
23
|
+
return typeof character === "string" && /\s/u.test(character);
|
|
24
|
+
}
|
|
25
|
+
function findPhpLineBoundary(source, index) {
|
|
26
|
+
const newlineIndex = source.indexOf("\n", index);
|
|
27
|
+
if (newlineIndex === -1) {
|
|
28
|
+
return {
|
|
29
|
+
contentEnd: source.endsWith("\r") ? source.length - 1 : source.length,
|
|
30
|
+
nextStart: source.length,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
contentEnd: source[newlineIndex - 1] === "\r" ? newlineIndex - 1 : newlineIndex,
|
|
35
|
+
nextStart: newlineIndex + 1,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function parsePhpHeredocStart(source, index) {
|
|
39
|
+
if (!source.startsWith("<<<", index)) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
let cursor = index + 3;
|
|
43
|
+
while (isPhpHorizontalWhitespace(source[cursor])) {
|
|
44
|
+
cursor += 1;
|
|
45
|
+
}
|
|
46
|
+
const quote = source[cursor] === "'" || source[cursor] === '"' ? source[cursor] : "";
|
|
47
|
+
if (quote) {
|
|
48
|
+
cursor += 1;
|
|
49
|
+
}
|
|
50
|
+
if (!isPhpIdentifierStart(source[cursor])) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const delimiterStart = cursor;
|
|
54
|
+
cursor += 1;
|
|
55
|
+
while (isPhpIdentifierPart(source[cursor])) {
|
|
56
|
+
cursor += 1;
|
|
57
|
+
}
|
|
58
|
+
const delimiter = source.slice(delimiterStart, cursor);
|
|
59
|
+
if (quote) {
|
|
60
|
+
if (source[cursor] !== quote) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
cursor += 1;
|
|
64
|
+
}
|
|
65
|
+
const lineBoundary = findPhpLineBoundary(source, cursor);
|
|
66
|
+
if (source.slice(cursor, lineBoundary.contentEnd).trim() !== "") {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
contentStart: lineBoundary.nextStart,
|
|
71
|
+
delimiter,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function findPhpHeredocClosingEnd(source, index, delimiter) {
|
|
75
|
+
if (!isPhpLineStart(source, index)) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
let cursor = index;
|
|
79
|
+
while (isPhpHorizontalWhitespace(source[cursor])) {
|
|
80
|
+
cursor += 1;
|
|
81
|
+
}
|
|
82
|
+
if (!source.startsWith(delimiter, cursor)) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
cursor += delimiter.length;
|
|
86
|
+
if (isPhpIdentifierPart(source[cursor])) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
let continuationCursor = cursor;
|
|
90
|
+
while (isPhpHorizontalWhitespace(source[continuationCursor])) {
|
|
91
|
+
continuationCursor += 1;
|
|
92
|
+
}
|
|
93
|
+
const continuation = source[continuationCursor];
|
|
94
|
+
if (continuationCursor >= source.length ||
|
|
95
|
+
continuation === "\r" ||
|
|
96
|
+
continuation === "\n" ||
|
|
97
|
+
!isPhpIdentifierPart(continuation)) {
|
|
98
|
+
return cursor;
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
function skipPhpCallTrivia(source, index) {
|
|
103
|
+
let cursor = index;
|
|
104
|
+
while (cursor < source.length) {
|
|
105
|
+
while (isPhpWhitespace(source[cursor])) {
|
|
106
|
+
cursor += 1;
|
|
107
|
+
}
|
|
108
|
+
if (source[cursor] === "/" && source[cursor + 1] === "*") {
|
|
109
|
+
const commentEnd = source.indexOf("*/", cursor + 2);
|
|
110
|
+
if (commentEnd === -1) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
cursor = commentEnd + 2;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (source[cursor] === "/" && source[cursor + 1] === "/") {
|
|
117
|
+
cursor = findPhpLineBoundary(source, cursor + 2).nextStart;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (source[cursor] === "#" && source[cursor + 1] !== "[") {
|
|
121
|
+
cursor = findPhpLineBoundary(source, cursor + 1).nextStart;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
return cursor;
|
|
125
|
+
}
|
|
126
|
+
return cursor;
|
|
127
|
+
}
|
|
128
|
+
function matchesPhpFunctionCallAt(source, index, functionName) {
|
|
129
|
+
if (!source.startsWith(functionName, index)) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
if (isPhpIdentifierPart(source[index - 1])) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
const cursor = index + functionName.length;
|
|
136
|
+
if (isPhpIdentifierPart(source[cursor])) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
const callStart = skipPhpCallTrivia(source, cursor);
|
|
140
|
+
return callStart !== null && source[callStart] === "(";
|
|
141
|
+
}
|
|
142
|
+
function createPhpScannerState() {
|
|
143
|
+
return {
|
|
144
|
+
heredocDelimiter: "",
|
|
145
|
+
mode: "code",
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function advancePhpScanner(source, index, state) {
|
|
149
|
+
const character = source[index];
|
|
150
|
+
if (state.mode === "heredoc") {
|
|
151
|
+
const closingEnd = findPhpHeredocClosingEnd(source, index, state.heredocDelimiter);
|
|
152
|
+
if (closingEnd !== null) {
|
|
153
|
+
state.mode = "code";
|
|
154
|
+
state.heredocDelimiter = "";
|
|
155
|
+
return { ambiguous: false, inCode: false, index: closingEnd };
|
|
156
|
+
}
|
|
157
|
+
const nextLineStart = findPhpLineBoundary(source, index).nextStart;
|
|
158
|
+
if (nextLineStart <= index) {
|
|
159
|
+
return { ambiguous: true, inCode: false, index };
|
|
160
|
+
}
|
|
161
|
+
return { ambiguous: false, inCode: false, index: nextLineStart };
|
|
162
|
+
}
|
|
163
|
+
if (state.mode === "single-quoted" || state.mode === "double-quoted") {
|
|
164
|
+
const quote = state.mode === "single-quoted" ? "'" : '"';
|
|
165
|
+
if (character === "\\") {
|
|
166
|
+
return { ambiguous: false, inCode: false, index: index + 2 };
|
|
167
|
+
}
|
|
168
|
+
if (character === quote) {
|
|
169
|
+
state.mode = "code";
|
|
170
|
+
}
|
|
171
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
172
|
+
}
|
|
173
|
+
if (state.mode === "line-comment") {
|
|
174
|
+
if (character === "\r" || character === "\n") {
|
|
175
|
+
state.mode = "code";
|
|
176
|
+
}
|
|
177
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
178
|
+
}
|
|
179
|
+
if (state.mode === "block-comment") {
|
|
180
|
+
if (character === "*" && source[index + 1] === "/") {
|
|
181
|
+
state.mode = "code";
|
|
182
|
+
return { ambiguous: false, inCode: false, index: index + 2 };
|
|
183
|
+
}
|
|
184
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
185
|
+
}
|
|
186
|
+
if (character === "'") {
|
|
187
|
+
state.mode = "single-quoted";
|
|
188
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
189
|
+
}
|
|
190
|
+
if (character === '"') {
|
|
191
|
+
state.mode = "double-quoted";
|
|
192
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
193
|
+
}
|
|
194
|
+
if (character === "/" && source[index + 1] === "/") {
|
|
195
|
+
state.mode = "line-comment";
|
|
196
|
+
return { ambiguous: false, inCode: false, index: index + 2 };
|
|
197
|
+
}
|
|
198
|
+
if (character === "#" && source[index + 1] !== "[") {
|
|
199
|
+
state.mode = "line-comment";
|
|
200
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
201
|
+
}
|
|
202
|
+
if (character === "/" && source[index + 1] === "*") {
|
|
203
|
+
state.mode = "block-comment";
|
|
204
|
+
return { ambiguous: false, inCode: false, index: index + 2 };
|
|
205
|
+
}
|
|
206
|
+
if (character === "<") {
|
|
207
|
+
const heredocStart = parsePhpHeredocStart(source, index);
|
|
208
|
+
if (heredocStart) {
|
|
209
|
+
state.mode = "heredoc";
|
|
210
|
+
state.heredocDelimiter = heredocStart.delimiter;
|
|
211
|
+
return {
|
|
212
|
+
ambiguous: false,
|
|
213
|
+
inCode: false,
|
|
214
|
+
index: heredocStart.contentStart,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return { ambiguous: false, inCode: true, index };
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Detect a PHP function call outside strings, comments, heredoc, and nowdoc blocks.
|
|
222
|
+
*
|
|
223
|
+
* @param source PHP source to scan.
|
|
224
|
+
* @param functionName Literal PHP function identifier to find.
|
|
225
|
+
* @returns Whether `source` contains a code-mode call to `functionName`.
|
|
226
|
+
*/
|
|
227
|
+
export function hasPhpFunctionCall(source, functionName) {
|
|
228
|
+
const scanner = createPhpScannerState();
|
|
229
|
+
let index = 0;
|
|
230
|
+
while (index < source.length) {
|
|
231
|
+
const scan = advancePhpScanner(source, index, scanner);
|
|
232
|
+
if (scan.ambiguous) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
if (!scan.inCode) {
|
|
236
|
+
index = scan.index;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (matchesPhpFunctionCallAt(source, index, functionName)) {
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
index += 1;
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Locate a PHP function body without counting braces in non-code regions.
|
|
248
|
+
*
|
|
249
|
+
* @param source PHP source to scan.
|
|
250
|
+
* @param functionName Literal PHP function identifier to locate.
|
|
251
|
+
* @param options Range options such as trailing newline inclusion.
|
|
252
|
+
* @returns The matched {@link PhpFunctionRange}, or `null` when no safe range exists.
|
|
253
|
+
*/
|
|
10
254
|
export function findPhpFunctionRange(source, functionName, options = {}) {
|
|
11
255
|
const signaturePattern = new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\([^)]*\\)\\s*(?::\\s*[^{};]+)?\\s*\\{`, "u");
|
|
12
256
|
const signatureMatch = signaturePattern.exec(source);
|
|
@@ -20,13 +264,25 @@ export function findPhpFunctionRange(source, functionName, options = {}) {
|
|
|
20
264
|
}
|
|
21
265
|
const openBraceIndex = functionStart + openBraceOffset;
|
|
22
266
|
let depth = 0;
|
|
23
|
-
|
|
267
|
+
const scanner = createPhpScannerState();
|
|
268
|
+
let index = openBraceIndex;
|
|
269
|
+
while (index < source.length) {
|
|
270
|
+
const scan = advancePhpScanner(source, index, scanner);
|
|
271
|
+
if (scan.ambiguous) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
if (!scan.inCode) {
|
|
275
|
+
index = scan.index;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
24
278
|
const character = source[index];
|
|
25
279
|
if (character === "{") {
|
|
26
280
|
depth += 1;
|
|
281
|
+
index += 1;
|
|
27
282
|
continue;
|
|
28
283
|
}
|
|
29
284
|
if (character !== "}") {
|
|
285
|
+
index += 1;
|
|
30
286
|
continue;
|
|
31
287
|
}
|
|
32
288
|
depth -= 1;
|
|
@@ -43,6 +299,7 @@ export function findPhpFunctionRange(source, functionName, options = {}) {
|
|
|
43
299
|
start: functionStart,
|
|
44
300
|
};
|
|
45
301
|
}
|
|
302
|
+
index += 1;
|
|
46
303
|
}
|
|
47
304
|
return null;
|
|
48
305
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
1
|
import { promises as fsp } from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { execSync } from "node:child_process";
|
|
@@ -12,6 +11,7 @@ import { formatNonEmptyTargetDirectoryError, } from "./scaffold-bootstrap.js";
|
|
|
12
11
|
import { stringifyBuiltInBlockJsonDocument, } from "./built-in-block-artifacts.js";
|
|
13
12
|
import { copyInterpolatedDirectory } from "./template-render.js";
|
|
14
13
|
import { formatInstallCommand, transformPackageManagerText, } from "./package-managers.js";
|
|
14
|
+
import { pathExists, readOptionalUtf8File, } from "./fs-async.js";
|
|
15
15
|
import { normalizePackageJson } from "./scaffold-package-manager-files.js";
|
|
16
16
|
export { applyWorkspaceMigrationCapability, isOfficialWorkspaceProject, } from "./scaffold-bootstrap.js";
|
|
17
17
|
import { replaceRepositoryReferencePlaceholders, resolveScaffoldRepositoryReference, } from "./scaffold-repository-reference.js";
|
|
@@ -28,10 +28,7 @@ const LOCKFILES = {
|
|
|
28
28
|
yarn: ["yarn.lock"],
|
|
29
29
|
};
|
|
30
30
|
export async function ensureDirectory(targetDir, allowExisting = false) {
|
|
31
|
-
|
|
32
|
-
await fsp.mkdir(targetDir, { recursive: true });
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
31
|
+
await fsp.mkdir(targetDir, { recursive: true });
|
|
35
32
|
if (allowExisting) {
|
|
36
33
|
return;
|
|
37
34
|
}
|
|
@@ -68,7 +65,7 @@ async function writeBuiltInCodeArtifacts(targetDir, codeArtifacts) {
|
|
|
68
65
|
await fsp.writeFile(destinationPath, artifact.source, "utf8");
|
|
69
66
|
}
|
|
70
67
|
}
|
|
71
|
-
function resolveScaffoldGeneratorNodeModulesPath() {
|
|
68
|
+
async function resolveScaffoldGeneratorNodeModulesPath() {
|
|
72
69
|
const projectToolsPackageRoot = path.resolve(__dirname, "..", "..");
|
|
73
70
|
const candidates = [
|
|
74
71
|
path.join(projectToolsPackageRoot, "node_modules"),
|
|
@@ -76,7 +73,7 @@ function resolveScaffoldGeneratorNodeModulesPath() {
|
|
|
76
73
|
path.resolve(projectToolsPackageRoot, "..", "..", "node_modules"),
|
|
77
74
|
];
|
|
78
75
|
for (const candidate of candidates) {
|
|
79
|
-
if (
|
|
76
|
+
if (await pathExists(path.join(candidate, "typia", "package.json"))) {
|
|
80
77
|
return candidate;
|
|
81
78
|
}
|
|
82
79
|
}
|
|
@@ -84,11 +81,11 @@ function resolveScaffoldGeneratorNodeModulesPath() {
|
|
|
84
81
|
}
|
|
85
82
|
async function withEphemeralScaffoldNodeModules(targetDir, callback) {
|
|
86
83
|
const targetNodeModulesPath = path.join(targetDir, "node_modules");
|
|
87
|
-
if (
|
|
84
|
+
if (await pathExists(targetNodeModulesPath)) {
|
|
88
85
|
await callback();
|
|
89
86
|
return;
|
|
90
87
|
}
|
|
91
|
-
const sourceNodeModulesPath = resolveScaffoldGeneratorNodeModulesPath();
|
|
88
|
+
const sourceNodeModulesPath = await resolveScaffoldGeneratorNodeModulesPath();
|
|
92
89
|
if (!sourceNodeModulesPath) {
|
|
93
90
|
throw new Error("Unable to resolve a node_modules directory with typia for scaffold-time REST artifact generation.");
|
|
94
91
|
}
|
|
@@ -134,9 +131,7 @@ export async function normalizePackageManagerFiles(targetDir, packageManagerId)
|
|
|
134
131
|
await fsp.writeFile(yarnRcPath, "nodeLinker: node-modules\n", "utf8");
|
|
135
132
|
return;
|
|
136
133
|
}
|
|
137
|
-
|
|
138
|
-
await fsp.rm(yarnRcPath, { force: true });
|
|
139
|
-
}
|
|
134
|
+
await fsp.rm(yarnRcPath, { force: true });
|
|
140
135
|
}
|
|
141
136
|
export async function removeQueryLoopPlaceholderFiles(projectDir, templateId) {
|
|
142
137
|
if (templateId !== "query-loop") {
|
|
@@ -153,10 +148,7 @@ export async function removeUnexpectedLockfiles(targetDir, packageManagerId) {
|
|
|
153
148
|
if (keep.has(filename)) {
|
|
154
149
|
return;
|
|
155
150
|
}
|
|
156
|
-
|
|
157
|
-
if (fs.existsSync(filePath)) {
|
|
158
|
-
await fsp.rm(filePath, { force: true });
|
|
159
|
-
}
|
|
151
|
+
await fsp.rm(path.join(targetDir, filename), { force: true });
|
|
160
152
|
}));
|
|
161
153
|
}
|
|
162
154
|
/**
|
|
@@ -251,7 +243,7 @@ export async function applyBuiltInScaffoldProjectFiles({ projectDir, templateDir
|
|
|
251
243
|
title: "Finalizing scaffold output",
|
|
252
244
|
});
|
|
253
245
|
const readmePath = path.join(projectDir, "README.md");
|
|
254
|
-
if (!
|
|
246
|
+
if (!(await pathExists(readmePath))) {
|
|
255
247
|
await fsp.writeFile(readmePath, readmeContent ??
|
|
256
248
|
buildReadme(templateId, variables, packageManager, {
|
|
257
249
|
withMigrationUi,
|
|
@@ -260,9 +252,7 @@ export async function applyBuiltInScaffoldProjectFiles({ projectDir, templateDir
|
|
|
260
252
|
}), "utf8");
|
|
261
253
|
}
|
|
262
254
|
const gitignorePath = path.join(projectDir, ".gitignore");
|
|
263
|
-
const existingGitignore =
|
|
264
|
-
? await fsp.readFile(gitignorePath, "utf8")
|
|
265
|
-
: "";
|
|
255
|
+
const existingGitignore = await readOptionalUtf8File(gitignorePath) ?? "";
|
|
266
256
|
await fsp.writeFile(gitignorePath, mergeTextLines(gitignoreContent ?? buildGitignore(), existingGitignore), "utf8");
|
|
267
257
|
await normalizePackageJson(projectDir, packageManager);
|
|
268
258
|
await applyGeneratedProjectDxPackageJson({
|
|
@@ -8,6 +8,7 @@ import { getPackageVersions } from "./package-versions.js";
|
|
|
8
8
|
import { getStarterManifestFiles, stringifyStarterManifest } from "./starter-manifests.js";
|
|
9
9
|
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, PROJECT_TOOLS_PACKAGE_ROOT, } from "./template-registry.js";
|
|
10
10
|
import { getScaffoldTemplateVariableGroups } from "./scaffold-template-variable-groups.js";
|
|
11
|
+
import { pathExists } from "./fs-async.js";
|
|
11
12
|
const EPHEMERAL_NODE_MODULES_LINK_TYPE = process.platform === "win32" ? "junction" : "dir";
|
|
12
13
|
/**
|
|
13
14
|
* Ensures the scaffold target directory exists and is empty unless explicitly allowed.
|
|
@@ -17,10 +18,7 @@ const EPHEMERAL_NODE_MODULES_LINK_TYPE = process.platform === "win32" ? "junctio
|
|
|
17
18
|
* @returns A promise that resolves once the directory precondition is satisfied.
|
|
18
19
|
*/
|
|
19
20
|
export async function ensureScaffoldDirectory(targetDir, allowExisting = false) {
|
|
20
|
-
|
|
21
|
-
await fsp.mkdir(targetDir, { recursive: true });
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
21
|
+
await fsp.mkdir(targetDir, { recursive: true });
|
|
24
22
|
if (allowExisting) {
|
|
25
23
|
return;
|
|
26
24
|
}
|
|
@@ -165,14 +163,14 @@ export async function applyWorkspaceMigrationCapability(projectDir, packageManag
|
|
|
165
163
|
*
|
|
166
164
|
* @returns The first matching path, or `null` when no candidate contains `typia`.
|
|
167
165
|
*/
|
|
168
|
-
function resolveScaffoldGeneratorNodeModulesPath() {
|
|
166
|
+
async function resolveScaffoldGeneratorNodeModulesPath() {
|
|
169
167
|
const candidates = [
|
|
170
168
|
path.join(PROJECT_TOOLS_PACKAGE_ROOT, "node_modules"),
|
|
171
169
|
path.resolve(PROJECT_TOOLS_PACKAGE_ROOT, "..", ".."),
|
|
172
170
|
path.resolve(PROJECT_TOOLS_PACKAGE_ROOT, "..", "..", "node_modules"),
|
|
173
171
|
];
|
|
174
172
|
for (const candidate of candidates) {
|
|
175
|
-
if (
|
|
173
|
+
if (await pathExists(path.join(candidate, "typia", "package.json"))) {
|
|
176
174
|
return candidate;
|
|
177
175
|
}
|
|
178
176
|
}
|
|
@@ -193,11 +191,11 @@ function resolveScaffoldGeneratorNodeModulesPath() {
|
|
|
193
191
|
*/
|
|
194
192
|
async function withEphemeralScaffoldNodeModules(targetDir, callback) {
|
|
195
193
|
const targetNodeModulesPath = path.join(targetDir, "node_modules");
|
|
196
|
-
if (
|
|
194
|
+
if (await pathExists(targetNodeModulesPath)) {
|
|
197
195
|
await callback();
|
|
198
196
|
return;
|
|
199
197
|
}
|
|
200
|
-
const sourceNodeModulesPath = resolveScaffoldGeneratorNodeModulesPath();
|
|
198
|
+
const sourceNodeModulesPath = await resolveScaffoldGeneratorNodeModulesPath();
|
|
201
199
|
if (!sourceNodeModulesPath) {
|
|
202
200
|
throw new Error("Unable to resolve a node_modules directory with typia for scaffold-time REST artifact generation.");
|
|
203
201
|
}
|
|
@@ -32,6 +32,16 @@ export interface ScaffoldCompatibilityConfig {
|
|
|
32
32
|
requiredFeatures: string[];
|
|
33
33
|
runtimeGates: string[];
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Optional hooks for surfacing user-authored compatibility header repairs.
|
|
37
|
+
*/
|
|
38
|
+
export interface UpdatePluginHeaderCompatibilityOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Receives warnings when a malformed existing plugin header value is
|
|
41
|
+
* replaced by the resolved policy floor.
|
|
42
|
+
*/
|
|
43
|
+
onWarning?: (warning: string) => void;
|
|
44
|
+
}
|
|
35
45
|
/**
|
|
36
46
|
* Baseline headers used by scaffold output before optional features are added.
|
|
37
47
|
*/
|
|
@@ -61,7 +71,9 @@ export declare function renderScaffoldCompatibilityConfig(policy: ScaffoldCompat
|
|
|
61
71
|
/**
|
|
62
72
|
* Patch a generated plugin bootstrap header without lowering custom floors.
|
|
63
73
|
*
|
|
64
|
-
* Preserves the original header line endings while replacing empty
|
|
65
|
-
*
|
|
74
|
+
* Preserves the original header line endings while replacing empty version
|
|
75
|
+
* strings with policy values. Malformed user-authored values are reported
|
|
76
|
+
* through `options.onWarning`; without a warning handler they throw instead of
|
|
77
|
+
* falling back silently.
|
|
66
78
|
*/
|
|
67
|
-
export declare function updatePluginHeaderCompatibility(source: string, policy: ScaffoldCompatibilityPolicy): string;
|
|
79
|
+
export declare function updatePluginHeaderCompatibility(source: string, policy: ScaffoldCompatibilityPolicy, options?: UpdatePluginHeaderCompatibilityOptions): string;
|