asljs-locator 0.1.1 → 0.1.2

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 CHANGED
@@ -1,20 +1,51 @@
1
- # MIT License
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 and associated documentation files (the "Software"), to deal in
7
- the Software without restriction, including without limitation the rights to
8
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
- the Software, and to permit persons to whom the Software is furnished to do so,
10
- subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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
@@ -68,6 +74,10 @@ Patterns match files only. A pattern ending with `/` would name folders and
68
74
  throws; to treat a folder as an item, point at a file inside it, e.g.
69
75
  `packages/*/package.json`.
70
76
 
77
+ Two path helpers come with it: `toPosixPath(path)`, the path with forward
78
+ slashes, and `isInside(folder, target, { orSame? })`, whether `target` is
79
+ `folder` or below it; with `orSame: false`, only below it.
80
+
71
81
  ## Further reading
72
82
 
73
83
  - [Architecture][2] for where this sits among the packages.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { GitIgnore } from './git-ignore.js';
2
2
  export { LocationResolver, toPatterns, type Location, type LocationFilter } from './location.js';
3
- export { toPosixPath } from './paths.js';
3
+ export { isInside, toPosixPath } from './paths.js';
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export { GitIgnore } from './git-ignore.js';
2
2
  export { LocationResolver, toPatterns } from './location.js';
3
- export { toPosixPath } from './paths.js';
3
+ export { isInside, toPosixPath } from './paths.js';
@@ -44,11 +44,23 @@ 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
+ * How a pattern resolves: `absolute`, a path of the file system, such as
49
+ * `/tmp/notes/*.md` or `C:/notes/*.md`; `root`, from the root, such as
50
+ * `/docs/**`; or `base`, from the base path.
51
+ *
52
+ * A pattern beginning with `/` is from the root, unless its first segment
53
+ * is no glob, the root has no entry of that name, and the file system root
54
+ * has one: `/tmp/x.md`, given under a root without a `tmp`, names the file
55
+ * where it is.
56
+ */
57
+ private kindOf;
47
58
  /**
48
59
  * The absolute paths the patterns match.
49
60
  *
50
- * A pattern beginning with `/` is expanded from the root, the rest from the
51
- * base path, so one call can carry both kinds.
61
+ * An absolute pattern is expanded where it is, one beginning with `/` from
62
+ * the root, the rest from the base path, so one call can carry every kind;
63
+ * see `kindOf`.
52
64
  *
53
65
  * Dotfiles are matched, because `check` matches them and the two have to
54
66
  * agree: without it a path such as `.remarkrc.mjs` would be reported as
package/dist/location.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { glob } from 'glob';
2
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
@@ -98,11 +100,14 @@ export class LocationResolver {
98
100
  const normalisedTargetPath = path.normalize(path.resolve(targetPath));
99
101
  const normalisedBasePath = path.normalize(path.resolve(basePath));
100
102
  const matchesPattern = (pattern) => {
101
- const anchored = pattern.startsWith('/');
102
- const relativePath = path.relative(anchored
103
+ const kind = this.kindOf(pattern);
104
+ if (kind === 'absolute') {
105
+ return minimatch(toPosixPath(normalisedTargetPath), toPosixPath(pattern), { dot: true });
106
+ }
107
+ const relativePath = path.relative(kind === 'root'
103
108
  ? this.rootPath
104
109
  : normalisedBasePath, normalisedTargetPath);
105
- return minimatch(relativePath, anchored
110
+ return minimatch(relativePath, kind === 'root'
106
111
  ? pattern.slice(1)
107
112
  : pattern, { dot: true });
108
113
  };
@@ -115,11 +120,38 @@ export class LocationResolver {
115
120
  const result = this.applyFilters([normalisedTargetPath], filters);
116
121
  return result.length > 0;
117
122
  }
123
+ /**
124
+ * How a pattern resolves: `absolute`, a path of the file system, such as
125
+ * `/tmp/notes/*.md` or `C:/notes/*.md`; `root`, from the root, such as
126
+ * `/docs/**`; or `base`, from the base path.
127
+ *
128
+ * A pattern beginning with `/` is from the root, unless its first segment
129
+ * is no glob, the root has no entry of that name, and the file system root
130
+ * has one: `/tmp/x.md`, given under a root without a `tmp`, names the file
131
+ * where it is.
132
+ */
133
+ kindOf(pattern) {
134
+ if (!pattern.startsWith('/')) {
135
+ return path.isAbsolute(pattern)
136
+ ? 'absolute'
137
+ : 'base';
138
+ }
139
+ const first = pattern
140
+ .slice(1)
141
+ .split('/')[0];
142
+ return first !== ''
143
+ && !/[*?[\]{}()!+@]/.test(first)
144
+ && !existsSync(path.join(this.rootPath, first))
145
+ && existsSync(path.join(path.parse(this.rootPath).root, first))
146
+ ? 'absolute'
147
+ : 'root';
148
+ }
118
149
  /**
119
150
  * The absolute paths the patterns match.
120
151
  *
121
- * A pattern beginning with `/` is expanded from the root, the rest from the
122
- * base path, so one call can carry both kinds.
152
+ * An absolute pattern is expanded where it is, one beginning with `/` from
153
+ * the root, the rest from the base path, so one call can carry every kind;
154
+ * see `kindOf`.
123
155
  *
124
156
  * Dotfiles are matched, because `check` matches them and the two have to
125
157
  * agree: without it a path such as `.remarkrc.mjs` would be reported as
@@ -127,8 +159,18 @@ export class LocationResolver {
127
159
  */
128
160
  async expand(patterns, basePath) {
129
161
  const matches = [];
162
+ const absolutePatterns = patterns
163
+ .filter(pattern => this.kindOf(pattern)
164
+ === 'absolute')
165
+ .map(pattern => toPosixPath(pattern));
166
+ if (absolutePatterns.length > 0) {
167
+ matches.push(...await glob(absolutePatterns, { absolute: true,
168
+ dot: true,
169
+ nodir: true }));
170
+ }
130
171
  const rootPatterns = patterns
131
- .filter(pattern => pattern.startsWith('/'))
172
+ .filter(pattern => this.kindOf(pattern)
173
+ === 'root')
132
174
  .map(pattern => pattern.slice(1));
133
175
  if (rootPatterns.length > 0) {
134
176
  matches.push(...await glob(rootPatterns, { cwd: this.rootPath,
@@ -136,7 +178,8 @@ export class LocationResolver {
136
178
  dot: true,
137
179
  nodir: true }));
138
180
  }
139
- const basePatterns = patterns.filter(pattern => !pattern.startsWith('/'));
181
+ const basePatterns = patterns.filter(pattern => this.kindOf(pattern)
182
+ === 'base');
140
183
  if (basePatterns.length > 0) {
141
184
  matches.push(...await glob(basePatterns, { cwd: basePath,
142
185
  absolute: true,
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.1",
3
+ "version": "0.1.2",
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,21 +17,23 @@
14
17
  "files": [
15
18
  "dist/**",
16
19
  "README.md",
17
- "LICENSE.md"
20
+ "LICENSE.md",
21
+ "docs/**"
18
22
  ],
19
23
  "scripts": {
20
24
  "flint": "toolkit flint",
21
25
  "clean": "toolkit clean",
22
- "build": "tsc -p tsconfig.build.json",
23
- "build:dist": "tsc -p tsconfig.dist.json",
24
- "typecheck": "tsc -p tsconfig.build.json --noEmit",
26
+ "build": "npm run codegen --if-present && tsc -p tsconfig.build.json",
27
+ "build:dist": "npm run codegen --if-present && tsc -p tsconfig.dist.json",
28
+ "typecheck": "npm run codegen --if-present && tsc -p tsconfig.build.json --noEmit",
25
29
  "guard:clean-git": "toolkit ensure-clean-working-directory",
26
- "prepack": "npm run clean && npm run build && npm run test && npm run build:dist && npm run guard:clean-git",
30
+ "prepack": "npm run clean && npm run build:dist && npm run build && npm run test && npm run guard:clean-git",
27
31
  "postpublish": "toolkit tag-release-revision",
28
- "test": "node --test --enable-source-maps build/**/*.test.js",
29
- "test:watch": "node --watch --test build/**/*.test.js",
32
+ "test": "node --test --enable-source-maps \"build/**/*.test.js\"",
33
+ "test:watch": "node --watch --test \"build/**/*.test.js\"",
34
+ "coverage": "npm run build && node --test --experimental-test-coverage --enable-source-maps --test-coverage-exclude=\"**/*.test.js\" \"build/**/*.test.js\"",
30
35
  "release:patch": "toolkit release-patch",
31
- "all": "npm i && npm run clean && npm run build:dist && npm run flint && npm run typecheck && npm run build && npm run test"
36
+ "all": "npm run clean && npm run build:dist && npm run flint && npm run typecheck && npm run build && npm run test"
32
37
  },
33
38
  "keywords": [
34
39
  "glob",
@@ -46,7 +51,7 @@
46
51
  "url": "git+https://github.com/AlexandriteSoftware/asljs.git"
47
52
  },
48
53
  "author": "\"Alex Netkachov\" <alex.netkachov@gmail.com>",
49
- "license": "MIT",
54
+ "license": "SEE LICENSE IN LICENSE.md",
50
55
  "dependencies": {
51
56
  "asljs-logging": "^0.1.1",
52
57
  "glob": "^13.0.6",