Skip to content

Commit b3107ae

Browse files
committed
New: initial docs
1 parent e574858 commit b3107ae

File tree

4 files changed

+340
-1
lines changed

4 files changed

+340
-1
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ __VS Code users:__ once you've added a single import anywhere in your project, y
2222

2323
Looking for more detailed documentation? You'll find it under the [docs](./docs) folder.
2424

25+
* [Our API](./docs/api/)
2526
* [Our CHANGELOG](CHANGELOG.md)
2627
* [Our software license](LICENSE.md)
2728
* [All contributors to date](AUTHORS.md)

docs/api/Filepath.md

+270
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
```typescript
2+
// how to import into your own code
3+
import { Filepath } from "@safelytyped/filepath";
4+
5+
// our base class and interface can be found here:
6+
import { RefinedString, Value } from "@safelytyped/core-types";
7+
8+
/**
9+
* `Filepath` is a safe type. It represents a path to an item on
10+
* a (possibly local) filesystem.
11+
*
12+
* It acts as a wrapper around NodeJS's `path` module.
13+
*
14+
* @category Filepath
15+
*/
16+
export class Filepath extends RefinedString implements Value<string> {
17+
18+
/**
19+
* `Constructor` creates a new `Filepath`.
20+
*
21+
* @param input
22+
* The data we need to build a Filepath.
23+
* @param onError
24+
* If `input` fails validation, we pass an {@link AppError}
25+
* to `onError()`.
26+
* @param base
27+
* Use this to keep track of a parent path of some kind.
28+
* @param pathApi
29+
* Use this if you want to pass in your own implementation (e.g. for
30+
* unit testing)
31+
*/
32+
public constructor(
33+
input: string,
34+
{
35+
onError = THROW_THE_ERROR,
36+
pathApi = path,
37+
base
38+
}: Partial<MakeFilepathOptions> = {}
39+
);
40+
41+
/**
42+
* `base` is for keeping track of any path that this Filepath
43+
* is built from, relative to, and the like.
44+
*
45+
* Useful for tracking parent paths when resolving '$ref' entries
46+
* in JSON schema and the like.
47+
*/
48+
public readonly base: string;
49+
50+
/**
51+
* `pathApi` is the API to use for all path operations.
52+
*
53+
* By default, this is the Node JS `path` module for your platform
54+
* (`path.windows` or `path.posix`).
55+
*/
56+
public readonly pathApi: string;
57+
58+
// =======================================================================
59+
//
60+
// pathApi wrappers
61+
//
62+
// -----------------------------------------------------------------------
63+
64+
/**
65+
* `basename()` is a wrapper around NodeJS's `path.basename()`.
66+
*
67+
* `basename` returns the last portion of a path, similar to the Unix
68+
* `basename` command. Trailing directory separators are ignored,
69+
* see {@link PathApi.sep}.
70+
*
71+
* @param ext
72+
* An optional file extension to strip (if present).
73+
* @returns
74+
* The last portion of the Filepath. If the Filepath` ends in `ext`,
75+
* that is stripped too.
76+
*/
77+
public basename(ext?: string): string;
78+
79+
/**
80+
* `dirname()` is a wrapper around NodeJS's `path.dirname()`.
81+
*
82+
* The returned Filepath will have the same `base` path and
83+
* same `pathApi` that this Filepath does.
84+
*
85+
* `dirname()` returns the directory name of `path`, similiar to
86+
* the UNIX `dirname` command. Trailing directory separators are
87+
* ignored, see {@link PathApi.sep}
88+
*
89+
* @returns
90+
* Everything but the last portion of `path`.
91+
*/
92+
public dirname(
93+
{
94+
onError = THROW_THE_ERROR,
95+
pathApi = this.#_pathApi,
96+
base = this.#_base
97+
}: Partial<MakeFilepathOptions> = {}
98+
): Filepath;
99+
100+
/**
101+
* `extname` is a wrapper around NodeJS's `path.extname()`.
102+
*
103+
* `extname()` returns the extension of the Filepath, from the last
104+
* occurance of the `.` character in the last segment of the path to
105+
* the end of the string.
106+
*
107+
* If there is no `.` in the last segment of the path, or if the only
108+
* `.` character is the first character of the last segment (ie a
109+
* UNIX dotfile with no extension), an empty string is returned.
110+
*
111+
* @returns
112+
* - the file extension (starting with a `.`) on success
113+
* - an empty string otherwise
114+
*/
115+
public extname(): string;
116+
117+
/**
118+
* `isAbsolute()` is a wrapper around NodeJS's `path.isAbsolute()`.
119+
*
120+
* `isAbsolute()` determines if this Filepath is an absolute path or not.
121+
*
122+
* An absolute path is a path that starts with a filesystem root
123+
* segment. Absolute paths are the opposite of relative paths: they
124+
* don't rely on the current working directory.
125+
*
126+
* @returns
127+
* - `true` if this Filepath is an absolute path.
128+
* - `false` if this Filepath is a relative path.
129+
*/
130+
public isAbsolute(): boolean;
131+
132+
/**
133+
* `join()` is a wrapper around NodeJS's `path.join()`.
134+
*
135+
* `join()` combines this Filepath and all the given segments into a
136+
* single string, using {@link PathApi.sep} as the delimiter, and then
137+
* normalises that string.
138+
*
139+
* Zero-length path segments are ignored.
140+
*
141+
* If the normalised string is empty, `join()` returns `.` (the
142+
* current working directory).
143+
*
144+
* The returned Filepath will have the same `base` path and
145+
* same `pathApi` that this Filepath does.
146+
*
147+
* @returns
148+
* The assembled path. Guaranteed never to be empty.
149+
*/
150+
public join(...paths: string[]): Filepath;
151+
152+
/**
153+
* `parse()` is a wrapper around NodeJS's `path.parse()`.
154+
*
155+
* `parse()` breaks down this Filepath into separate parts. Trailing
156+
* directory separators are ignored.
157+
*
158+
* The returned object can include any / all of:
159+
*
160+
* - `root`: the root of the filesystem, if the path is absolute
161+
* - `dir`: all the root and folder segments of the path
162+
* - `base`: the filename segment of the path
163+
* - `name`: the filename segment of the path, minus the extension
164+
* - `ext`: the extension segment of the path, starting with a `.`
165+
*
166+
* All of these are strings. Use {@link PathApi.format} to convert
167+
* this back into a single path string.
168+
*
169+
* @returns
170+
* The breakdown of this Filepath.
171+
*/
172+
public parse(): path.ParsedPath;
173+
174+
/**
175+
* `relative()` is a wrapper around NodeJS's `path.relative()`.
176+
*
177+
* `relative()` calculates a relative path between `from` and `to`.
178+
* `from` is this Filepath.
179+
*
180+
* If `from` and `to` point to the same path (after calling
181+
* {@link PathApi.resolve} on them both), and empty string is returned.
182+
*
183+
* @param to
184+
* Where do you want the relative path to go to?
185+
* @returns
186+
* - An empty string if `from` and `to` point to the same path.
187+
* - The relative path otherwise.
188+
*/
189+
public relative(to: Filepath): string;
190+
191+
/**
192+
* `resolve()` is a wrapper around NodeJS's `path.resolve()`.
193+
*
194+
* `resolve()` combines this Filepath and the given path segments into
195+
* an absolute path.
196+
*
197+
* It works from right-to-left (from the last segment backwards), and
198+
* it stops as soon as an absolute path has been constructed.
199+
*
200+
* Zero-length path segments are ignored.
201+
*
202+
* If the assembled path is a relative path, it is treated as a relative
203+
* path to the current working directory, and converted into an
204+
* absolute path.
205+
*
206+
* The assembled path is normalised before being returned. Trailing
207+
* path separators are removed.
208+
*
209+
* If no path segments are passed in, `resolve()` returns the absolute
210+
* path of resolving `this.valueOf()` against `this.base`. It's
211+
* a good way to get the absolute path of this Filepath. If this Filepath
212+
* doesn't have a `base`, it uses the process's current working directory.
213+
*
214+
* The returned Filepath will have the same `base` path and
215+
* same `pathApi` that this Filepath does.
216+
*
217+
* @returns
218+
* The assembled, normalised Filepath. Guaranteed to be an absolute path.
219+
*/
220+
public resolve(...paths: string[]): Filepath;
221+
222+
/**
223+
* `toNamespacedPath()` is a wrapper around NodeJS's
224+
* `path.toNamespacedPath()`.
225+
*
226+
* `toNamespacedPath()` behaves differently on Windows and on POSIX.
227+
*
228+
* On Windows, it converts this Filepath to the equivalent namespace-prefixed
229+
* path: https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#namespaces.
230+
*
231+
* On POSIX, a new Filepath is returned, containing the unmodified
232+
* `Filepath.toValue()`.
233+
*
234+
* @returns
235+
* - on Windows, the namespaced path.
236+
* - on POSIX, the unmodified path.
237+
*/
238+
public toNamespacedPath(): Filepath;
239+
240+
// =======================================================================
241+
//
242+
// INHERITED METHODS
243+
//
244+
// -----------------------------------------------------------------------
245+
246+
/**
247+
* `[Symbol.toPrimitive]()` supports Javascript auto-conversion to
248+
* a string or number.
249+
*/
250+
public [ Symbol.toPrimitive ](hint: PrimitiveHint): string | number;
251+
252+
/**
253+
* valueOf() returns the wrapped value.
254+
*
255+
* For types passed by reference, we do NOT return a clone of any kind.
256+
* You have to be careful not to accidentally change this value.
257+
*
258+
* @returns the data that is stored in this object.
259+
*/
260+
public valueOf(): T {
261+
return this._value;
262+
}
263+
264+
/**
265+
* implementsValue() is a helper method for the {@link isValue} type guard
266+
* function.
267+
*/
268+
public implementsValue(): this is Value<T>;
269+
}
270+
```

docs/api/README.md

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# API Docs
2+
3+
Welcome to `@safelytyped/filepath`!
4+
5+
- [Introduction](#introduction)
6+
- [What Is A Safe Type?](#what-is-a-safe-type)
7+
- [Why Use Safe Types?](#why-use-safe-types)
8+
- [Why A Filepath Type?](#why-a-filepath-type)
9+
- [Our Goals](#our-goals)
10+
- [Our Design Criteria](#our-design-criteria)
11+
12+
## Introduction
13+
14+
### What Is A Safe Type?
15+
16+
A _safe type_ is a type that can only ever hold legal values.
17+
18+
For example, `Filepath` is a safe type because it can only ever hold a valid filesystem path (it just might not be valid on your computer!).
19+
20+
### Why Use Safe Types?
21+
22+
If your function accepts a _safe type_, your function doesn't need to do its own [defensive programming][Defensive Programming] for robustness.
23+
24+
**Safe types do the defensive programming for you.**
25+
26+
On top of that, we build our _safe types_ using [written coding standards][SafelyTyped Coding Standards].
27+
28+
### Why A Filepath Type?
29+
30+
You might be wondering why you should use `Filepath`, instead of a plain ol' `string`. There's a few reasons why:
31+
32+
* Compile-time checking:
33+
34+
If you just use strings everywhere, the compiler can't tell a file path from a username. It certainly can't tell you when you accidentally use a username instead of a file path.
35+
36+
If you use types everywhere, then the compiler can spot when you've passed the wrong kind of string into a function.
37+
38+
* A means to deliver functionality into business logic:
39+
40+
If you pass typed values into your business logic, it can call methods on those types without having to know whether it's working with a _Filepath_ or a _URL_.
41+
42+
_Value objects_ can implement [protocols][Protocol] (interfaces that describe behaviours). And business logic can consume and use objects based solely on their protocols, without caring whether they're a _Filepath_ or a _URL_.
43+
44+
* Runtime extensibility:
45+
46+
Along with [protocols][Protocol], _value objects_ can be augmented at runtime with [extensions][Extension]. You don't have to submit a pull request to our repo and wait for us to merge it; you can create your own extension and use it locally straight away.
47+
48+
## Our Goals
49+
50+
The purpose of this library is to give us the smallest possible, viable [Filepath](Filepath) safe type that adds value to our code.
51+
52+
### Our Design Criteria
53+
54+
There's a couple of things that we wanted out of `Filepath`.
55+
56+
* [Filepath][Filepath]'s API should be very familiar to anyone who already knows NodeJS's `path` module.
57+
* [Filepath][Filepath] should be useful for tracking a data location: a path that's built from a mix of a base path and a location relative to that base path.
58+
59+
[Filepath]: ./Filepath.md
60+
[Branded Type]: https://github.com/SafelyTyped/ts-coding-standards/blob/master/glossary/branded-type.md
61+
[Defensive Programming]: https://github.com/SafelyTyped/ts-coding-standards/blob/master/glossary/defensive-programming.md
62+
[Extension]: https://github.com/SafelyTyped/ts-coding-standards/blob/master/glossary/extension.md
63+
[Protocol]: https://github.com/SafelyTyped/ts-coding-standards/blob/master/glossary/protocol.md
64+
[Refined Type]: https://github.com/SafelyTyped/ts-coding-standards/blob/master/glossary/refined-type.md
65+
[SafelyTyped on GitHub]: https://github.com/SafelyTyped/
66+
[SafelyTyped Coding Standards]: https://github.com/SafelyTyped/ts-coding-standards/

src/v1/Filepath/Filepath.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,9 @@ export class Filepath extends RefinedString {
302302
* path separators are removed.
303303
*
304304
* If no path segments are passed in, `resolve()` returns the absolute
305-
* path of the current working directory.
305+
* path of resolving `this.valueOf()` against `this.base`. It's
306+
* a good way to get the absolute path of this Filepath. If this Filepath
307+
* doesn't have a `base`, it uses the process's current working directory.
306308
*
307309
* The returned Filepath will have the same `base` path and
308310
* same `pathApi` that this Filepath does.

0 commit comments

Comments
 (0)