asljs-locator 0.1.1 → 0.1.3
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/LICENSE.md +51 -20
- package/README.md +15 -0
- package/dist/git-ignore.d.ts +18 -5
- package/dist/git-ignore.js +52 -46
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/location.d.ts +25 -3
- package/dist/location.js +123 -45
- package/dist/paths.d.ts +7 -0
- package/dist/paths.js +14 -0
- package/package.json +24 -11
package/LICENSE.md
CHANGED
|
@@ -1,20 +1,51 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Alexandrite Software Ltd
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
-
this software
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
# Permissive No-Derivatives License 1.0
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alexandrite Software Ltd.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software (the "Software") to:
|
|
7
|
+
|
|
8
|
+
1. Use the Software for any purpose, including commercial purposes.
|
|
9
|
+
2. Copy and redistribute the Software, including as part of another application,
|
|
10
|
+
library, or package.
|
|
11
|
+
3. Modify the Software for private or internal use.
|
|
12
|
+
|
|
13
|
+
Subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
## 1. No Distribution of Modified Versions
|
|
16
|
+
|
|
17
|
+
Modified versions of the Software, or substantial portions thereof, must not be
|
|
18
|
+
distributed without prior written permission from the copyright holder.
|
|
19
|
+
|
|
20
|
+
Routine build transformations, including minification, transpilation, and
|
|
21
|
+
bundling, are permitted for distribution, provided they do not intentionally
|
|
22
|
+
alter the Software's functionality.
|
|
23
|
+
|
|
24
|
+
## 2. Licence and Copyright Preservation
|
|
25
|
+
|
|
26
|
+
All distributed copies of the Software must retain this copyright notice and
|
|
27
|
+
licence.
|
|
28
|
+
|
|
29
|
+
The Software itself, including copies incorporated into other applications,
|
|
30
|
+
libraries, or packages, remains subject to this licence.
|
|
31
|
+
|
|
32
|
+
The Software must not be sublicensed or redistributed under a different licence.
|
|
33
|
+
|
|
34
|
+
## 3. Use in Other Software
|
|
35
|
+
|
|
36
|
+
Applications and libraries that merely use, import, link to, or bundle the
|
|
37
|
+
Software are not considered modified versions of the Software and may be
|
|
38
|
+
distributed under any licence.
|
|
39
|
+
|
|
40
|
+
Such use, incorporation, or bundling does not grant permission to relicense the
|
|
41
|
+
Software or distribute modified versions of it.
|
|
42
|
+
|
|
43
|
+
### 4. No Warranty
|
|
44
|
+
|
|
45
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
46
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
47
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
48
|
+
|
|
49
|
+
IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR
|
|
50
|
+
OTHER LIABILITY ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR ITS
|
|
51
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -18,6 +18,12 @@ with. Any other pattern resolves against the base path given to the call. That
|
|
|
18
18
|
pair is what lets one vocabulary describe both a repository-wide location and a
|
|
19
19
|
location relative to the file that declared it.
|
|
20
20
|
|
|
21
|
+
An absolute path is a pattern where it is, inside the root or not:
|
|
22
|
+
`C:/notes/*.md` always, and `/tmp/notes/*.md` when the root has no `tmp` and the
|
|
23
|
+
file system root has one. A pattern beginning with `/` whose first segment the
|
|
24
|
+
root has, or which begins with a glob, such as `/docs/**` or `/**/*.md`, stays
|
|
25
|
+
from the root.
|
|
26
|
+
|
|
21
27
|
## Scope
|
|
22
28
|
|
|
23
29
|
Finding paths, and answering whether a path belongs to a location. Reading the
|
|
@@ -53,6 +59,11 @@ const files = await resolver.resolve(process.cwd(), {
|
|
|
53
59
|
`resolve` returns absolute paths, deduplicated and sorted. Give it one location
|
|
54
60
|
or several; several are merged.
|
|
55
61
|
|
|
62
|
+
The excludes and the filters apply while the tree is walked, not to what it
|
|
63
|
+
found: a folder that `.gitignore` ignores, or whose every file an exclude such
|
|
64
|
+
as `**/node_modules/**` takes away, is not entered at all. That is what keeps
|
|
65
|
+
locating cheap in a tree full of `node_modules` and build output.
|
|
66
|
+
|
|
56
67
|
`check` answers the same question for a single path without walking the tree,
|
|
57
68
|
which is what makes it usable per file:
|
|
58
69
|
|
|
@@ -68,6 +79,10 @@ Patterns match files only. A pattern ending with `/` would name folders and
|
|
|
68
79
|
throws; to treat a folder as an item, point at a file inside it, e.g.
|
|
69
80
|
`packages/*/package.json`.
|
|
70
81
|
|
|
82
|
+
Two path helpers come with it: `toPosixPath(path)`, the path with forward
|
|
83
|
+
slashes, and `isInside(folder, target, { orSame? })`, whether `target` is
|
|
84
|
+
`folder` or below it; with `orSame: false`, only below it.
|
|
85
|
+
|
|
71
86
|
## Further reading
|
|
72
87
|
|
|
73
88
|
- [Architecture][2] for where this sits among the packages.
|
package/dist/git-ignore.d.ts
CHANGED
|
@@ -2,16 +2,29 @@ import { Logger } from 'asljs-logging';
|
|
|
2
2
|
/**
|
|
3
3
|
* Whether a path is ignored by a `.gitignore`.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* A path is tested against the `.gitignore` of every folder above it, because
|
|
6
|
+
* a nested `.gitignore` applies to its own subtree and git reads them all.
|
|
7
|
+
* Each folder's file is read once, and a path is tested only against the
|
|
8
|
+
* folders it is in, however many other folders were read.
|
|
9
|
+
*
|
|
10
|
+
* A path ending with the separator is a folder, tested as `name/`, as git
|
|
11
|
+
* matches a pattern such as `build/`; `LocationResolver` asks so before it
|
|
12
|
+
* walks into a folder, and skips the folder when it is ignored, since git
|
|
13
|
+
* ignores everything inside an ignored folder.
|
|
8
14
|
*/
|
|
9
15
|
export declare class GitIgnore {
|
|
10
16
|
private logger;
|
|
17
|
+
/**
|
|
18
|
+
* The matcher of each folder read, `null` for a folder without a
|
|
19
|
+
* `.gitignore`.
|
|
20
|
+
*/
|
|
11
21
|
private matchers;
|
|
12
|
-
private visitedPaths;
|
|
13
22
|
constructor(logger: Logger);
|
|
14
23
|
isIgnored(targetPath: string): boolean;
|
|
15
24
|
filter(paths: string[]): string[];
|
|
16
|
-
|
|
25
|
+
/**
|
|
26
|
+
* The matcher of the `.gitignore` of a folder, read the first time it is
|
|
27
|
+
* asked for; `null` when the folder has none.
|
|
28
|
+
*/
|
|
29
|
+
private matcherOf;
|
|
17
30
|
}
|
package/dist/git-ignore.js
CHANGED
|
@@ -1,81 +1,87 @@
|
|
|
1
1
|
import ignore from 'ignore';
|
|
2
|
-
import { readFileSync
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { toPosixPath } from './paths.js';
|
|
5
5
|
/**
|
|
6
6
|
* Whether a path is ignored by a `.gitignore`.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* A path is tested against the `.gitignore` of every folder above it, because
|
|
9
|
+
* a nested `.gitignore` applies to its own subtree and git reads them all.
|
|
10
|
+
* Each folder's file is read once, and a path is tested only against the
|
|
11
|
+
* folders it is in, however many other folders were read.
|
|
12
|
+
*
|
|
13
|
+
* A path ending with the separator is a folder, tested as `name/`, as git
|
|
14
|
+
* matches a pattern such as `build/`; `LocationResolver` asks so before it
|
|
15
|
+
* walks into a folder, and skips the folder when it is ignored, since git
|
|
16
|
+
* ignores everything inside an ignored folder.
|
|
11
17
|
*/
|
|
12
18
|
export class GitIgnore {
|
|
13
19
|
logger;
|
|
20
|
+
/**
|
|
21
|
+
* The matcher of each folder read, `null` for a folder without a
|
|
22
|
+
* `.gitignore`.
|
|
23
|
+
*/
|
|
14
24
|
matchers;
|
|
15
|
-
visitedPaths;
|
|
16
25
|
constructor(logger) {
|
|
17
26
|
this.logger = logger;
|
|
18
|
-
this.matchers =
|
|
19
|
-
this.visitedPaths = new Set();
|
|
27
|
+
this.matchers = new Map();
|
|
20
28
|
}
|
|
21
29
|
isIgnored(targetPath) {
|
|
22
30
|
if (!path.isAbsolute(targetPath)) {
|
|
23
31
|
throw new Error(`Path must be absolute: ${targetPath}`);
|
|
24
32
|
}
|
|
25
33
|
const isDirectory = targetPath.endsWith(path.sep);
|
|
26
|
-
const normalisedPath = path.normalize(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
this.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const testRelativePath = isDirectory
|
|
40
|
-
? relativePath + '/'
|
|
41
|
-
: relativePath;
|
|
42
|
-
if (matcher.matcher.ignores(testRelativePath)) {
|
|
43
|
-
return true;
|
|
34
|
+
const normalisedPath = path.normalize(isDirectory
|
|
35
|
+
? targetPath.slice(0, -path.sep.length)
|
|
36
|
+
: targetPath);
|
|
37
|
+
let folder = path.dirname(normalisedPath);
|
|
38
|
+
while (folder !== null) {
|
|
39
|
+
const matcher = this.matcherOf(folder);
|
|
40
|
+
if (matcher !== null) {
|
|
41
|
+
const relativePath = toPosixPath(path.relative(folder, normalisedPath));
|
|
42
|
+
if (matcher.ignores(isDirectory
|
|
43
|
+
? relativePath + '/'
|
|
44
|
+
: relativePath)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
44
47
|
}
|
|
48
|
+
const parent = path.dirname(folder);
|
|
49
|
+
folder =
|
|
50
|
+
parent === folder
|
|
51
|
+
? null
|
|
52
|
+
: parent;
|
|
45
53
|
}
|
|
46
54
|
return false;
|
|
47
55
|
}
|
|
48
56
|
filter(paths) {
|
|
49
57
|
return paths.filter(filePath => !this.isIgnored(filePath));
|
|
50
58
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
/**
|
|
60
|
+
* The matcher of the `.gitignore` of a folder, read the first time it is
|
|
61
|
+
* asked for; `null` when the folder has none.
|
|
62
|
+
*/
|
|
63
|
+
matcherOf(folder) {
|
|
64
|
+
const known = this.matchers.get(folder);
|
|
65
|
+
if (known !== undefined) {
|
|
66
|
+
return known;
|
|
54
67
|
}
|
|
55
|
-
|
|
56
|
-
.add(directoryPath);
|
|
57
|
-
const gitIgnorePath = path.join(directoryPath, '.gitignore');
|
|
58
|
-
let gitIgnoreExists = false;
|
|
68
|
+
let content = null;
|
|
59
69
|
try {
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
content =
|
|
71
|
+
readFileSync(path.join(folder, '.gitignore'), 'utf8');
|
|
62
72
|
}
|
|
63
73
|
catch (err) {
|
|
64
74
|
const code = err.code;
|
|
65
|
-
|
|
75
|
+
// A folder without one, or a `.gitignore` that is a folder.
|
|
76
|
+
if (code !== 'ENOENT'
|
|
77
|
+
&& code !== 'EISDIR') {
|
|
66
78
|
throw err;
|
|
67
79
|
}
|
|
68
80
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
matcher });
|
|
75
|
-
}
|
|
76
|
-
const parentPath = path.dirname(directoryPath);
|
|
77
|
-
if (parentPath !== directoryPath) {
|
|
78
|
-
this.loadMatchers(parentPath);
|
|
79
|
-
}
|
|
81
|
+
const matcher = content === null
|
|
82
|
+
? null
|
|
83
|
+
: ignore().add(content);
|
|
84
|
+
this.matchers.set(folder, matcher);
|
|
85
|
+
return matcher;
|
|
80
86
|
}
|
|
81
87
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/location.d.ts
CHANGED
|
@@ -44,16 +44,38 @@ export declare class LocationResolver {
|
|
|
44
44
|
check(targetPath: string, basePath: string, location: Location | readonly Location[]): Promise<boolean>;
|
|
45
45
|
private resolveOne;
|
|
46
46
|
private checkOne;
|
|
47
|
+
/**
|
|
48
|
+
* Whether `filters` asks for `GitIgnore`, the only filter there is; an
|
|
49
|
+
* unknown name throws.
|
|
50
|
+
*/
|
|
51
|
+
private usesGitIgnore;
|
|
52
|
+
/**
|
|
53
|
+
* Whether an absolute path matches a pattern, resolved as `kindOf` says:
|
|
54
|
+
* where it is, from the root, or from the base path. `resolve` and `check`
|
|
55
|
+
* both match with it, so they agree.
|
|
56
|
+
*/
|
|
57
|
+
private patternMatcher;
|
|
58
|
+
/**
|
|
59
|
+
* How a pattern resolves: `absolute`, a path of the file system, such as
|
|
60
|
+
* `/tmp/notes/*.md` or `C:/notes/*.md`; `root`, from the root, such as
|
|
61
|
+
* `/docs/**`; or `base`, from the base path.
|
|
62
|
+
*
|
|
63
|
+
* A pattern beginning with `/` is from the root, unless its first segment
|
|
64
|
+
* is no glob, the root has no entry of that name, and the file system root
|
|
65
|
+
* has one: `/tmp/x.md`, given under a root without a `tmp`, names the file
|
|
66
|
+
* where it is.
|
|
67
|
+
*/
|
|
68
|
+
private kindOf;
|
|
47
69
|
/**
|
|
48
70
|
* The absolute paths the patterns match.
|
|
49
71
|
*
|
|
50
|
-
*
|
|
51
|
-
* base path, so one call can carry
|
|
72
|
+
* An absolute pattern is expanded where it is, one beginning with `/` from
|
|
73
|
+
* the root, the rest from the base path, so one call can carry every kind;
|
|
74
|
+
* see `kindOf`.
|
|
52
75
|
*
|
|
53
76
|
* Dotfiles are matched, because `check` matches them and the two have to
|
|
54
77
|
* agree: without it a path such as `.remarkrc.mjs` would be reported as
|
|
55
78
|
* belonging to a location that never finds it.
|
|
56
79
|
*/
|
|
57
80
|
private expand;
|
|
58
|
-
private applyFilters;
|
|
59
81
|
}
|
package/dist/location.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { glob } from 'glob';
|
|
2
|
-
import {
|
|
2
|
+
import { Minimatch } from 'minimatch';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import { GitIgnore } from './git-ignore.js';
|
|
6
|
+
import { toPosixPath } from './paths.js';
|
|
5
7
|
/** The patterns of a location, however they were given. */
|
|
6
8
|
export function toPatterns(location) {
|
|
7
9
|
const patterns = [...location.pattern === undefined
|
|
@@ -47,7 +49,11 @@ export class LocationResolver {
|
|
|
47
49
|
const results = [];
|
|
48
50
|
for (const one of toLocations(location)) {
|
|
49
51
|
const resolved = await this.resolveOne(basePath, one);
|
|
50
|
-
|
|
52
|
+
// One at a time: spreading tens of thousands of paths into `push`
|
|
53
|
+
// exceeds the call stack.
|
|
54
|
+
for (const match of resolved) {
|
|
55
|
+
results.push(match);
|
|
56
|
+
}
|
|
51
57
|
}
|
|
52
58
|
const unique = [...new Set(results)];
|
|
53
59
|
unique.sort((a, b) => a.localeCompare(b));
|
|
@@ -78,11 +84,31 @@ export class LocationResolver {
|
|
|
78
84
|
const normalisedBasePath = path.normalize(path.resolve(basePath));
|
|
79
85
|
assertFilePatterns([...patterns,
|
|
80
86
|
...exclude]);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
// Excludes and the filters are applied as the tree is walked, not to what
|
|
88
|
+
// it found: a folder whose every file is excluded, as by
|
|
89
|
+
// `**/node_modules/**`, or which `.gitignore` ignores, is not entered.
|
|
90
|
+
const excluded = exclude.map(pattern => this.patternMatcher(pattern, normalisedBasePath));
|
|
91
|
+
// `<folder>/**` excludes everything in the folders `<folder>` matches.
|
|
92
|
+
const excludedFolders = exclude
|
|
93
|
+
.filter(pattern => pattern.endsWith('/**'))
|
|
94
|
+
.map(pattern => pattern.slice(0, -'/**'.length))
|
|
95
|
+
.filter(folder => folder !== ''
|
|
96
|
+
&& folder !== '/')
|
|
97
|
+
.map(folder => this.patternMatcher(folder, normalisedBasePath));
|
|
98
|
+
const gitIgnore = this.usesGitIgnore(filters);
|
|
99
|
+
const ignore = { ignored: (walked) => {
|
|
100
|
+
const fullPath = walked.fullpath();
|
|
101
|
+
return excluded.some(matches => matches(fullPath))
|
|
102
|
+
|| gitIgnore
|
|
103
|
+
&& this.gitIgnore.isIgnored(fullPath);
|
|
104
|
+
},
|
|
105
|
+
childrenIgnored: (walked) => {
|
|
106
|
+
const fullPath = walked.fullpath();
|
|
107
|
+
return excludedFolders.some(matches => matches(fullPath))
|
|
108
|
+
|| gitIgnore
|
|
109
|
+
&& this.gitIgnore.isIgnored(fullPath + path.sep);
|
|
110
|
+
} };
|
|
111
|
+
const result = [...new Set(await this.expand(patterns, normalisedBasePath, ignore))];
|
|
86
112
|
result.sort((a, b) => a.localeCompare(b));
|
|
87
113
|
this.logger.trace('LocationResolver.resolve() => %d matches', result.length);
|
|
88
114
|
return result;
|
|
@@ -97,66 +123,118 @@ export class LocationResolver {
|
|
|
97
123
|
...exclude]);
|
|
98
124
|
const normalisedTargetPath = path.normalize(path.resolve(targetPath));
|
|
99
125
|
const normalisedBasePath = path.normalize(path.resolve(basePath));
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
126
|
+
const matches = (pattern) => this.patternMatcher(pattern, normalisedBasePath)(normalisedTargetPath);
|
|
127
|
+
return patterns.some(matches)
|
|
128
|
+
&& !exclude.some(matches)
|
|
129
|
+
&& !(this.usesGitIgnore(filters)
|
|
130
|
+
&& this.gitIgnore.isIgnored(normalisedTargetPath));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Whether `filters` asks for `GitIgnore`, the only filter there is; an
|
|
134
|
+
* unknown name throws.
|
|
135
|
+
*/
|
|
136
|
+
usesGitIgnore(filters) {
|
|
137
|
+
for (const filter of filters) {
|
|
138
|
+
if (filter.name !== 'GitIgnore') {
|
|
139
|
+
throw new Error(`Unknown filter: ${filter.name}`);
|
|
140
|
+
}
|
|
111
141
|
}
|
|
112
|
-
|
|
113
|
-
|
|
142
|
+
return filters.length > 0;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Whether an absolute path matches a pattern, resolved as `kindOf` says:
|
|
146
|
+
* where it is, from the root, or from the base path. `resolve` and `check`
|
|
147
|
+
* both match with it, so they agree.
|
|
148
|
+
*/
|
|
149
|
+
patternMatcher(pattern, basePath) {
|
|
150
|
+
const kind = this.kindOf(pattern);
|
|
151
|
+
if (kind === 'absolute') {
|
|
152
|
+
const absolute = new Minimatch(toPosixPath(pattern), { dot: true });
|
|
153
|
+
return targetPath => absolute.match(toPosixPath(targetPath));
|
|
114
154
|
}
|
|
115
|
-
const
|
|
116
|
-
|
|
155
|
+
const from = kind === 'root'
|
|
156
|
+
? this.rootPath
|
|
157
|
+
: basePath;
|
|
158
|
+
const relative = new Minimatch(kind === 'root'
|
|
159
|
+
? pattern.slice(1)
|
|
160
|
+
: pattern, { dot: true });
|
|
161
|
+
return targetPath => relative.match(toPosixPath(path.relative(from, targetPath)));
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* How a pattern resolves: `absolute`, a path of the file system, such as
|
|
165
|
+
* `/tmp/notes/*.md` or `C:/notes/*.md`; `root`, from the root, such as
|
|
166
|
+
* `/docs/**`; or `base`, from the base path.
|
|
167
|
+
*
|
|
168
|
+
* A pattern beginning with `/` is from the root, unless its first segment
|
|
169
|
+
* is no glob, the root has no entry of that name, and the file system root
|
|
170
|
+
* has one: `/tmp/x.md`, given under a root without a `tmp`, names the file
|
|
171
|
+
* where it is.
|
|
172
|
+
*/
|
|
173
|
+
kindOf(pattern) {
|
|
174
|
+
if (!pattern.startsWith('/')) {
|
|
175
|
+
return path.isAbsolute(pattern)
|
|
176
|
+
? 'absolute'
|
|
177
|
+
: 'base';
|
|
178
|
+
}
|
|
179
|
+
const first = pattern
|
|
180
|
+
.slice(1)
|
|
181
|
+
.split('/')[0];
|
|
182
|
+
return first !== ''
|
|
183
|
+
&& !/[*?[\]{}()!+@]/.test(first)
|
|
184
|
+
&& !existsSync(path.join(this.rootPath, first))
|
|
185
|
+
&& existsSync(path.join(path.parse(this.rootPath).root, first))
|
|
186
|
+
? 'absolute'
|
|
187
|
+
: 'root';
|
|
117
188
|
}
|
|
118
189
|
/**
|
|
119
190
|
* The absolute paths the patterns match.
|
|
120
191
|
*
|
|
121
|
-
*
|
|
122
|
-
* base path, so one call can carry
|
|
192
|
+
* An absolute pattern is expanded where it is, one beginning with `/` from
|
|
193
|
+
* the root, the rest from the base path, so one call can carry every kind;
|
|
194
|
+
* see `kindOf`.
|
|
123
195
|
*
|
|
124
196
|
* Dotfiles are matched, because `check` matches them and the two have to
|
|
125
197
|
* agree: without it a path such as `.remarkrc.mjs` would be reported as
|
|
126
198
|
* belonging to a location that never finds it.
|
|
127
199
|
*/
|
|
128
|
-
async expand(patterns, basePath) {
|
|
200
|
+
async expand(patterns, basePath, ignore) {
|
|
129
201
|
const matches = [];
|
|
202
|
+
const absolutePatterns = patterns
|
|
203
|
+
.filter(pattern => this.kindOf(pattern)
|
|
204
|
+
=== 'absolute')
|
|
205
|
+
.map(pattern => toPosixPath(pattern));
|
|
206
|
+
if (absolutePatterns.length > 0) {
|
|
207
|
+
for (const match of await glob(absolutePatterns, { absolute: true,
|
|
208
|
+
dot: true,
|
|
209
|
+
nodir: true,
|
|
210
|
+
ignore })) {
|
|
211
|
+
matches.push(match);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
130
214
|
const rootPatterns = patterns
|
|
131
|
-
.filter(pattern =>
|
|
215
|
+
.filter(pattern => this.kindOf(pattern)
|
|
216
|
+
=== 'root')
|
|
132
217
|
.map(pattern => pattern.slice(1));
|
|
133
218
|
if (rootPatterns.length > 0) {
|
|
134
|
-
|
|
219
|
+
for (const match of await glob(rootPatterns, { cwd: this.rootPath,
|
|
135
220
|
absolute: true,
|
|
136
221
|
dot: true,
|
|
137
|
-
nodir: true
|
|
222
|
+
nodir: true,
|
|
223
|
+
ignore })) {
|
|
224
|
+
matches.push(match);
|
|
225
|
+
}
|
|
138
226
|
}
|
|
139
|
-
const basePatterns = patterns.filter(pattern =>
|
|
227
|
+
const basePatterns = patterns.filter(pattern => this.kindOf(pattern)
|
|
228
|
+
=== 'base');
|
|
140
229
|
if (basePatterns.length > 0) {
|
|
141
|
-
|
|
230
|
+
for (const match of await glob(basePatterns, { cwd: basePath,
|
|
142
231
|
absolute: true,
|
|
143
232
|
dot: true,
|
|
144
|
-
nodir: true
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
applyFilters(paths, filters) {
|
|
149
|
-
let result = paths;
|
|
150
|
-
for (const filter of filters) {
|
|
151
|
-
switch (filter.name) {
|
|
152
|
-
case 'GitIgnore':
|
|
153
|
-
result =
|
|
154
|
-
this.gitIgnore.filter(result);
|
|
155
|
-
break;
|
|
156
|
-
default:
|
|
157
|
-
throw new Error(`Unknown filter: ${filter.name}`);
|
|
233
|
+
nodir: true,
|
|
234
|
+
ignore })) {
|
|
235
|
+
matches.push(match);
|
|
158
236
|
}
|
|
159
237
|
}
|
|
160
|
-
return
|
|
238
|
+
return matches;
|
|
161
239
|
}
|
|
162
240
|
}
|
package/dist/paths.d.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
/** A path with forward slashes, which is what glob and .gitignore expect. */
|
|
2
2
|
export declare function toPosixPath(filePath: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Whether `target` is inside `folder`, or is `folder` itself; with
|
|
5
|
+
* `orSame: false`, only inside it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isInside(folder: string, target: string, options?: {
|
|
8
|
+
orSame?: boolean;
|
|
9
|
+
}): boolean;
|
package/dist/paths.js
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
1
2
|
/** A path with forward slashes, which is what glob and .gitignore expect. */
|
|
2
3
|
export function toPosixPath(filePath) {
|
|
3
4
|
return filePath.replaceAll('\\', '/');
|
|
4
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Whether `target` is inside `folder`, or is `folder` itself; with
|
|
8
|
+
* `orSame: false`, only inside it.
|
|
9
|
+
*/
|
|
10
|
+
export function isInside(folder, target, options = {}) {
|
|
11
|
+
const relative = path.relative(path.resolve(folder), path.resolve(target));
|
|
12
|
+
if (relative === '') {
|
|
13
|
+
return options.orSame !== false;
|
|
14
|
+
}
|
|
15
|
+
return relative !== '..'
|
|
16
|
+
&& !relative.startsWith(`..${path.sep}`)
|
|
17
|
+
&& !path.isAbsolute(relative);
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "asljs-locator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Locates files by glob patterns, exclusions and filters such as .gitignore.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"asljs": {
|
|
7
|
+
"type": "lib"
|
|
8
|
+
},
|
|
6
9
|
"main": "dist/index.js",
|
|
7
10
|
"types": "dist/index.d.ts",
|
|
8
11
|
"exports": {
|
|
@@ -14,22 +17,32 @@
|
|
|
14
17
|
"files": [
|
|
15
18
|
"dist/**",
|
|
16
19
|
"README.md",
|
|
17
|
-
"LICENSE.md"
|
|
20
|
+
"LICENSE.md",
|
|
21
|
+
"docs/**",
|
|
22
|
+
"!dist/*.tsbuildinfo"
|
|
18
23
|
],
|
|
19
24
|
"scripts": {
|
|
20
25
|
"flint": "toolkit flint",
|
|
21
|
-
"clean": "
|
|
22
|
-
"
|
|
23
|
-
"build
|
|
24
|
-
"
|
|
26
|
+
"clean:build": "node ../../scripts/cleanBuildOutDir.js",
|
|
27
|
+
"clean:dist": "node ../../scripts/cleanDistOutDir.js",
|
|
28
|
+
"compile:build": "npm run codegen --if-present && tsc -p tsconfig.build.json",
|
|
29
|
+
"compile:dist": "npm run codegen --if-present && tsc -p tsconfig.dist.json",
|
|
30
|
+
"build:dist": "node ../../scripts/buildDistDeps.js && npm run compile:dist",
|
|
31
|
+
"typecheck": "npm run codegen --if-present && tsc -p tsconfig.build.json --noEmit",
|
|
25
32
|
"guard:clean-git": "toolkit ensure-clean-working-directory",
|
|
26
|
-
"prepack": "npm run clean && npm run
|
|
33
|
+
"prepack": "npm run clean:build && npm run clean:dist && npm run build:dist && npm run compile:build && npm run test && npm run guard:clean-git",
|
|
27
34
|
"postpublish": "toolkit tag-release-revision",
|
|
28
|
-
"test": "node --test --enable-source-maps build/**/*.test.js",
|
|
29
|
-
"test:watch": "node --watch --test build/**/*.test.js",
|
|
35
|
+
"test": "node --test --enable-source-maps \"build/**/*.test.js\"",
|
|
36
|
+
"test:watch": "node --watch --test \"build/**/*.test.js\"",
|
|
37
|
+
"coverage": "npm run compile:build && node --test --experimental-test-coverage --enable-source-maps --test-coverage-exclude=\"**/*.test.js\" \"build/**/*.test.js\"",
|
|
30
38
|
"release:patch": "toolkit release-patch",
|
|
31
|
-
"
|
|
39
|
+
"build": "npm run clean:build && npm run flint && npm run typecheck && npm run compile:build && npm run test"
|
|
32
40
|
},
|
|
41
|
+
"distDependencies": [
|
|
42
|
+
"asljs-logging",
|
|
43
|
+
"asljs-testing",
|
|
44
|
+
"asljs-tmpdir"
|
|
45
|
+
],
|
|
33
46
|
"keywords": [
|
|
34
47
|
"glob",
|
|
35
48
|
"gitignore",
|
|
@@ -46,7 +59,7 @@
|
|
|
46
59
|
"url": "git+https://github.com/AlexandriteSoftware/asljs.git"
|
|
47
60
|
},
|
|
48
61
|
"author": "\"Alex Netkachov\" <alex.netkachov@gmail.com>",
|
|
49
|
-
"license": "
|
|
62
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
50
63
|
"dependencies": {
|
|
51
64
|
"asljs-logging": "^0.1.1",
|
|
52
65
|
"glob": "^13.0.6",
|