@omnifyjp/omnify 5.8.18 → 5.8.20
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/package.json +6 -6
- package/ts-dist/cli.js +46 -15
- package/ts-dist/php/pint-format.d.ts +51 -0
- package/ts-dist/php/pint-format.js +92 -0
- package/ts-dist/php/sibling-skip.d.ts +27 -14
- package/ts-dist/php/sibling-skip.js +39 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnifyjp/omnify",
|
|
3
|
-
"version": "5.8.
|
|
3
|
+
"version": "5.8.20",
|
|
4
4
|
"description": "Schema-driven code generation for Laravel, TypeScript, and SQL",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"zod": "^3.24.0"
|
|
37
37
|
},
|
|
38
38
|
"optionalDependencies": {
|
|
39
|
-
"@omnifyjp/omnify-darwin-arm64": "5.8.
|
|
40
|
-
"@omnifyjp/omnify-darwin-x64": "5.8.
|
|
41
|
-
"@omnifyjp/omnify-linux-x64": "5.8.
|
|
42
|
-
"@omnifyjp/omnify-linux-arm64": "5.8.
|
|
43
|
-
"@omnifyjp/omnify-win32-x64": "5.8.
|
|
39
|
+
"@omnifyjp/omnify-darwin-arm64": "5.8.20",
|
|
40
|
+
"@omnifyjp/omnify-darwin-x64": "5.8.20",
|
|
41
|
+
"@omnifyjp/omnify-linux-x64": "5.8.20",
|
|
42
|
+
"@omnifyjp/omnify-linux-arm64": "5.8.20",
|
|
43
|
+
"@omnifyjp/omnify-win32-x64": "5.8.20"
|
|
44
44
|
}
|
|
45
45
|
}
|
package/ts-dist/cli.js
CHANGED
|
@@ -30,7 +30,8 @@ import { parse as parseYaml } from 'yaml';
|
|
|
30
30
|
import { generateTypeScript } from './generator.js';
|
|
31
31
|
import { generatePhp, derivePhpConfig } from './php/index.js';
|
|
32
32
|
import { pruneOrphanServiceFiles } from './php/orphan-cleanup.js';
|
|
33
|
-
import { findSiblingWithSameBasename } from './php/sibling-skip.js';
|
|
33
|
+
import { findSiblingWithSameBasename, findAppRootAncestor } from './php/sibling-skip.js';
|
|
34
|
+
import { runPintFormat } from './php/pint-format.js';
|
|
34
35
|
import { resolveInput, sniffInputKind } from './input-resolver.js';
|
|
35
36
|
function resolveFromConfig(configPath) {
|
|
36
37
|
const raw = readFileSync(configPath, 'utf-8');
|
|
@@ -222,6 +223,7 @@ program
|
|
|
222
223
|
let phpSkipped = 0;
|
|
223
224
|
let phpSkippedSibling = 0;
|
|
224
225
|
const skippedSiblingExamples = [];
|
|
226
|
+
const writtenPaths = [];
|
|
225
227
|
for (const file of phpFiles) {
|
|
226
228
|
const filePath = resolve(configDir, file.path);
|
|
227
229
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
@@ -229,21 +231,29 @@ program
|
|
|
229
231
|
phpSkipped++;
|
|
230
232
|
continue;
|
|
231
233
|
}
|
|
232
|
-
// Issue #98 v5.8.18: skip user-editable stub
|
|
233
|
-
// sibling with the same basename exists
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
//
|
|
234
|
+
// Issue #98 v5.8.18 / #99 v5.8.19: skip user-editable stub
|
|
235
|
+
// when a project-owned sibling with the same basename exists
|
|
236
|
+
// elsewhere under the project's `app/` root. Two patterns
|
|
237
|
+
// covered:
|
|
238
|
+
// 1. Domain subfolder organization (#98 v5.8.18): project
|
|
239
|
+
// hand-writes `app/Http/Requests/Admin/BrandStoreRequest.php`;
|
|
240
|
+
// omnify wants to emit the flat default
|
|
241
|
+
// `app/Http/Requests/BrandStoreRequest.php`. The flat
|
|
242
|
+
// stub is unused noise.
|
|
243
|
+
// 2. v4 → v5 upgrade (#99 v5.8.19): project has v4 wrapper at
|
|
244
|
+
// `app/Services/Omnify/<Name>Service.php`; v5 wants to
|
|
245
|
+
// emit at the new modular path
|
|
246
|
+
// `app/Omnify/Services/<Name>Service.php`. Two wrappers
|
|
247
|
+
// compete; the v4 one carries the project's actual logic.
|
|
248
|
+
//
|
|
249
|
+
// Scope: scan from the project's `app/` directory (walked up
|
|
250
|
+
// from the target). Falls back to the file's parent dir when
|
|
251
|
+
// no `app/` ancestor is found (e.g. unusual rootPath layout).
|
|
252
|
+
// Only applies to user-editable files (`overwrite=false`); base
|
|
253
|
+
// files always regenerate.
|
|
245
254
|
if (!file.overwrite) {
|
|
246
|
-
const
|
|
255
|
+
const scanRoot = findAppRootAncestor(filePath) ?? dirname(filePath);
|
|
256
|
+
const collision = findSiblingWithSameBasename(scanRoot, basename(filePath), filePath);
|
|
247
257
|
if (collision) {
|
|
248
258
|
phpSkippedSibling++;
|
|
249
259
|
if (skippedSiblingExamples.length < 3) {
|
|
@@ -253,6 +263,7 @@ program
|
|
|
253
263
|
}
|
|
254
264
|
}
|
|
255
265
|
writeFileSync(filePath, file.content, 'utf-8');
|
|
266
|
+
writtenPaths.push(filePath);
|
|
256
267
|
if (file.overwrite || !existsSync(filePath)) {
|
|
257
268
|
phpOverwritten++;
|
|
258
269
|
}
|
|
@@ -274,6 +285,26 @@ program
|
|
|
274
285
|
console.log(` ... and ${phpSkippedSibling - skippedSiblingExamples.length} more`);
|
|
275
286
|
}
|
|
276
287
|
}
|
|
288
|
+
// Issue #100 v5.8.20: auto-format every written PHP file via
|
|
289
|
+
// Laravel Pint when it's installed in the project. Same preset
|
|
290
|
+
// the project's CI already uses, so codegen output stops failing
|
|
291
|
+
// `pint --test` and devs no longer need a manual sweep after
|
|
292
|
+
// every `omnify generate`. Silent no-op when Pint isn't found.
|
|
293
|
+
if (writtenPaths.length > 0) {
|
|
294
|
+
const pintResult = runPintFormat(writtenPaths);
|
|
295
|
+
if (pintResult.ran) {
|
|
296
|
+
if (pintResult.exitStatus === 0) {
|
|
297
|
+
console.log(` Formatted ${pintResult.formattedCount} PHP file(s) via Laravel Pint`);
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
console.warn(` [omnify] Laravel Pint exited with status ${pintResult.exitStatus}; generated files may not match project formatting preset.`);
|
|
301
|
+
if (pintResult.errorMessage) {
|
|
302
|
+
const trimmed = pintResult.errorMessage.trim().split('\n').slice(0, 5).join('\n');
|
|
303
|
+
console.warn(` ${trimmed}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
277
308
|
// Orphan cleanup: when a schema is removed, renamed, flipped from
|
|
278
309
|
// kind:object → kind:pivot, or opted out via `options.service: false`,
|
|
279
310
|
// its previously-emitted `*ServiceBase.php` becomes orphan and
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-format generated PHP files via Laravel Pint. Issue #100 v5.8.20.
|
|
3
|
+
*
|
|
4
|
+
* Reporter scenario (godx-jp/godx-kintai 2026-04-26 audit): every
|
|
5
|
+
* `omnify generate --force` produces 150+ PHP files that fail Laravel
|
|
6
|
+
* Pint's standard preset (`vendor/bin/pint --test`):
|
|
7
|
+
*
|
|
8
|
+
* - ordered_imports — `use` statements not alphabetised
|
|
9
|
+
* - fully_qualified_strict_types — exception types referenced via FQN
|
|
10
|
+
* - class_attributes_separation — missing blank lines between members
|
|
11
|
+
* - function_declaration — function-keyword spacing
|
|
12
|
+
* - unary_operator_spaces — `! \$x` vs `!\$x`
|
|
13
|
+
* - no_unused_imports — codegen leaves the occasional dangling import
|
|
14
|
+
* - single_line_empty_body — empty class bodies on multiple lines
|
|
15
|
+
* - self_static_accessor — `self::` vs `static::` on enums
|
|
16
|
+
* - blank_line_before_statement — enum `case` separators
|
|
17
|
+
*
|
|
18
|
+
* Auto-detect: when `<rootPath>/vendor/bin/pint` exists, run it on the
|
|
19
|
+
* files we just wrote. Same Pint preset the project's CI already uses,
|
|
20
|
+
* so no risk of formatter drift between codegen and CI checks.
|
|
21
|
+
*
|
|
22
|
+
* Opt-out: future config field; for now `chmod -x vendor/bin/pint` or
|
|
23
|
+
* deleting it disables the auto-run.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the `vendor/bin/pint` path for the project. Walks up from
|
|
27
|
+
* the first written file until we find a sibling `vendor/bin/pint`,
|
|
28
|
+
* stopping at filesystem root or after 12 levels (defensive against
|
|
29
|
+
* pathological inputs). Returns the absolute path or null.
|
|
30
|
+
*/
|
|
31
|
+
export declare function findPintBinary(anyWrittenPhpPath: string): string | null;
|
|
32
|
+
export interface PintFormatResult {
|
|
33
|
+
ran: boolean;
|
|
34
|
+
pintPath: string | null;
|
|
35
|
+
/** Files passed to pint. */
|
|
36
|
+
formattedCount: number;
|
|
37
|
+
/** Pint's exit status if it ran. 0 = success. */
|
|
38
|
+
exitStatus: number | null;
|
|
39
|
+
/** Captured stderr when pint failed (status != 0). */
|
|
40
|
+
errorMessage?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Run Laravel Pint on the given PHP files (absolute paths). No-op if
|
|
44
|
+
* Pint isn't installed in the project. Pint is invoked with `--quiet`
|
|
45
|
+
* so it stays out of the way unless something breaks.
|
|
46
|
+
*
|
|
47
|
+
* Pint takes a list of files (or directories) and formats them in
|
|
48
|
+
* place. It uses the project's `pint.json` config if present, else the
|
|
49
|
+
* Laravel preset.
|
|
50
|
+
*/
|
|
51
|
+
export declare function runPintFormat(writtenPhpPaths: string[]): PintFormatResult;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-format generated PHP files via Laravel Pint. Issue #100 v5.8.20.
|
|
3
|
+
*
|
|
4
|
+
* Reporter scenario (godx-jp/godx-kintai 2026-04-26 audit): every
|
|
5
|
+
* `omnify generate --force` produces 150+ PHP files that fail Laravel
|
|
6
|
+
* Pint's standard preset (`vendor/bin/pint --test`):
|
|
7
|
+
*
|
|
8
|
+
* - ordered_imports — `use` statements not alphabetised
|
|
9
|
+
* - fully_qualified_strict_types — exception types referenced via FQN
|
|
10
|
+
* - class_attributes_separation — missing blank lines between members
|
|
11
|
+
* - function_declaration — function-keyword spacing
|
|
12
|
+
* - unary_operator_spaces — `! \$x` vs `!\$x`
|
|
13
|
+
* - no_unused_imports — codegen leaves the occasional dangling import
|
|
14
|
+
* - single_line_empty_body — empty class bodies on multiple lines
|
|
15
|
+
* - self_static_accessor — `self::` vs `static::` on enums
|
|
16
|
+
* - blank_line_before_statement — enum `case` separators
|
|
17
|
+
*
|
|
18
|
+
* Auto-detect: when `<rootPath>/vendor/bin/pint` exists, run it on the
|
|
19
|
+
* files we just wrote. Same Pint preset the project's CI already uses,
|
|
20
|
+
* so no risk of formatter drift between codegen and CI checks.
|
|
21
|
+
*
|
|
22
|
+
* Opt-out: future config field; for now `chmod -x vendor/bin/pint` or
|
|
23
|
+
* deleting it disables the auto-run.
|
|
24
|
+
*/
|
|
25
|
+
import { existsSync, statSync } from 'node:fs';
|
|
26
|
+
import { spawnSync } from 'node:child_process';
|
|
27
|
+
import { dirname, join } from 'node:path';
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the `vendor/bin/pint` path for the project. Walks up from
|
|
30
|
+
* the first written file until we find a sibling `vendor/bin/pint`,
|
|
31
|
+
* stopping at filesystem root or after 12 levels (defensive against
|
|
32
|
+
* pathological inputs). Returns the absolute path or null.
|
|
33
|
+
*/
|
|
34
|
+
export function findPintBinary(anyWrittenPhpPath) {
|
|
35
|
+
let cur = dirname(anyWrittenPhpPath);
|
|
36
|
+
for (let i = 0; i < 12; i++) {
|
|
37
|
+
const candidate = join(cur, 'vendor', 'bin', 'pint');
|
|
38
|
+
if (existsSync(candidate)) {
|
|
39
|
+
try {
|
|
40
|
+
const st = statSync(candidate);
|
|
41
|
+
if (st.isFile())
|
|
42
|
+
return candidate;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// ignore stat errors and keep climbing
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const parent = dirname(cur);
|
|
49
|
+
if (parent === cur)
|
|
50
|
+
break;
|
|
51
|
+
cur = parent;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Run Laravel Pint on the given PHP files (absolute paths). No-op if
|
|
57
|
+
* Pint isn't installed in the project. Pint is invoked with `--quiet`
|
|
58
|
+
* so it stays out of the way unless something breaks.
|
|
59
|
+
*
|
|
60
|
+
* Pint takes a list of files (or directories) and formats them in
|
|
61
|
+
* place. It uses the project's `pint.json` config if present, else the
|
|
62
|
+
* Laravel preset.
|
|
63
|
+
*/
|
|
64
|
+
export function runPintFormat(writtenPhpPaths) {
|
|
65
|
+
if (writtenPhpPaths.length === 0) {
|
|
66
|
+
return { ran: false, pintPath: null, formattedCount: 0, exitStatus: null };
|
|
67
|
+
}
|
|
68
|
+
const pintPath = findPintBinary(writtenPhpPaths[0]);
|
|
69
|
+
if (!pintPath) {
|
|
70
|
+
return { ran: false, pintPath: null, formattedCount: 0, exitStatus: null };
|
|
71
|
+
}
|
|
72
|
+
// Pint's working dir should be the Laravel project root (where
|
|
73
|
+
// composer.json + pint.json live). The pint binary is at
|
|
74
|
+
// `<projectRoot>/vendor/bin/pint`, so projectRoot is two parents up.
|
|
75
|
+
const projectRoot = dirname(dirname(dirname(pintPath)));
|
|
76
|
+
// Pass each file as its own arg. Pint accepts multiple file/dir
|
|
77
|
+
// arguments; capping the batch is unnecessary up to a few hundred
|
|
78
|
+
// files (well under the OS arg limit).
|
|
79
|
+
const result = spawnSync(pintPath, ['--quiet', ...writtenPhpPaths], {
|
|
80
|
+
cwd: projectRoot,
|
|
81
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
82
|
+
encoding: 'utf-8',
|
|
83
|
+
});
|
|
84
|
+
const exitStatus = result.status ?? null;
|
|
85
|
+
return {
|
|
86
|
+
ran: true,
|
|
87
|
+
pintPath,
|
|
88
|
+
formattedCount: writtenPhpPaths.length,
|
|
89
|
+
exitStatus,
|
|
90
|
+
errorMessage: exitStatus !== 0 ? (result.stderr || result.stdout || undefined) : undefined,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -1,26 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* User-editable stub collision detection. Issue #98 v5.8.18.
|
|
2
|
+
* User-editable stub collision detection. Issue #98 v5.8.18 / #99 v5.8.19.
|
|
3
3
|
*
|
|
4
4
|
* Many Laravel projects organize Requests / Resources / Services by
|
|
5
|
-
* domain subfolder (`Admin/`, `Cms/`, `Audit
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* forcing the dev to delete it manually.
|
|
5
|
+
* domain subfolder (`Admin/`, `Cms/`, `Audit/`) BEFORE adopting omnify,
|
|
6
|
+
* OR they upgrade from v4 with wrappers at v4 paths
|
|
7
|
+
* (`app/Services/Omnify/<Name>Service.php`) and v5 wants to emit at
|
|
8
|
+
* the new modular path (`app/Omnify/Services/<Name>Service.php`). In
|
|
9
|
+
* both cases the auto-emitted user-editable stub is unused noise that
|
|
10
|
+
* regenerates every `omnify generate`, forcing the dev to delete it.
|
|
12
11
|
*
|
|
13
|
-
* Detection: scan
|
|
12
|
+
* Detection: scan a configurable root directory recursively for any
|
|
14
13
|
* file with the same basename. If found at a path != target, skip
|
|
15
14
|
* emission.
|
|
16
15
|
*
|
|
17
|
-
* Scope
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* Scope decision:
|
|
17
|
+
* - Wide scope (e.g. project's `app/`): finds cross-layer wrappers
|
|
18
|
+
* like the v4 → v5 upgrade case (#99).
|
|
19
|
+
* - Narrow scope (file's parent dir): finds same-layer siblings only,
|
|
20
|
+
* like a domain-organized `Admin/Brand.php` next to the flat
|
|
21
|
+
* default (#98 v5.8.18).
|
|
20
22
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
+
* The CLI passes whichever scope matches the layout. Bounded by
|
|
24
|
+
* `MAX_FILES_SCANNED` so a project with tens of thousands of files
|
|
25
|
+
* doesn't grind generate to a halt.
|
|
23
26
|
*/
|
|
27
|
+
/**
|
|
28
|
+
* Walk up from `filePath` looking for the closest ancestor directory
|
|
29
|
+
* named `app`. Returns its absolute path or null. Used by the CLI to
|
|
30
|
+
* pick a project-wide search root for sibling-skip — covers v4 → v5
|
|
31
|
+
* cross-layer wrappers (e.g. v4 wrapper at `app/Services/Omnify/<X>Service.php`
|
|
32
|
+
* vs v5 modular emit at `app/Omnify/Services/<X>Service.php`). Capped
|
|
33
|
+
* at 12 levels of climbing so we don't traverse the entire FS on a
|
|
34
|
+
* malformed input.
|
|
35
|
+
*/
|
|
36
|
+
export declare function findAppRootAncestor(filePath: string): string | null;
|
|
24
37
|
/**
|
|
25
38
|
* Scan `rootDir` recursively for a file whose basename matches
|
|
26
39
|
* `baseName`, excluding `excludePath` itself. Returns the first
|
|
@@ -1,29 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* User-editable stub collision detection. Issue #98 v5.8.18.
|
|
2
|
+
* User-editable stub collision detection. Issue #98 v5.8.18 / #99 v5.8.19.
|
|
3
3
|
*
|
|
4
4
|
* Many Laravel projects organize Requests / Resources / Services by
|
|
5
|
-
* domain subfolder (`Admin/`, `Cms/`, `Audit
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* forcing the dev to delete it manually.
|
|
5
|
+
* domain subfolder (`Admin/`, `Cms/`, `Audit/`) BEFORE adopting omnify,
|
|
6
|
+
* OR they upgrade from v4 with wrappers at v4 paths
|
|
7
|
+
* (`app/Services/Omnify/<Name>Service.php`) and v5 wants to emit at
|
|
8
|
+
* the new modular path (`app/Omnify/Services/<Name>Service.php`). In
|
|
9
|
+
* both cases the auto-emitted user-editable stub is unused noise that
|
|
10
|
+
* regenerates every `omnify generate`, forcing the dev to delete it.
|
|
12
11
|
*
|
|
13
|
-
* Detection: scan
|
|
12
|
+
* Detection: scan a configurable root directory recursively for any
|
|
14
13
|
* file with the same basename. If found at a path != target, skip
|
|
15
14
|
* emission.
|
|
16
15
|
*
|
|
17
|
-
* Scope
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* Scope decision:
|
|
17
|
+
* - Wide scope (e.g. project's `app/`): finds cross-layer wrappers
|
|
18
|
+
* like the v4 → v5 upgrade case (#99).
|
|
19
|
+
* - Narrow scope (file's parent dir): finds same-layer siblings only,
|
|
20
|
+
* like a domain-organized `Admin/Brand.php` next to the flat
|
|
21
|
+
* default (#98 v5.8.18).
|
|
20
22
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
+
* The CLI passes whichever scope matches the layout. Bounded by
|
|
24
|
+
* `MAX_FILES_SCANNED` so a project with tens of thousands of files
|
|
25
|
+
* doesn't grind generate to a halt.
|
|
23
26
|
*/
|
|
24
27
|
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
25
|
-
import { join } from 'node:path';
|
|
28
|
+
import { basename, dirname, join } from 'node:path';
|
|
26
29
|
const MAX_FILES_SCANNED = 5000;
|
|
30
|
+
/**
|
|
31
|
+
* Walk up from `filePath` looking for the closest ancestor directory
|
|
32
|
+
* named `app`. Returns its absolute path or null. Used by the CLI to
|
|
33
|
+
* pick a project-wide search root for sibling-skip — covers v4 → v5
|
|
34
|
+
* cross-layer wrappers (e.g. v4 wrapper at `app/Services/Omnify/<X>Service.php`
|
|
35
|
+
* vs v5 modular emit at `app/Omnify/Services/<X>Service.php`). Capped
|
|
36
|
+
* at 12 levels of climbing so we don't traverse the entire FS on a
|
|
37
|
+
* malformed input.
|
|
38
|
+
*/
|
|
39
|
+
export function findAppRootAncestor(filePath) {
|
|
40
|
+
let cur = dirname(filePath);
|
|
41
|
+
for (let i = 0; i < 12; i++) {
|
|
42
|
+
if (basename(cur) === 'app')
|
|
43
|
+
return cur;
|
|
44
|
+
const parent = dirname(cur);
|
|
45
|
+
if (parent === cur)
|
|
46
|
+
break;
|
|
47
|
+
cur = parent;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
27
51
|
/**
|
|
28
52
|
* Scan `rootDir` recursively for a file whose basename matches
|
|
29
53
|
* `baseName`, excluding `excludePath` itself. Returns the first
|