compose-paths 1.2.2 → 1.3.0
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/CHANGELOG.md +51 -4
- package/README.md +13 -11
- package/dist/browser/compose-paths.browser.js +2 -2
- package/dist/cjs/compose-paths.cjs.js +2 -2
- package/dist/esm/compose-paths.esm.js +2 -2
- package/index.d.ts +200 -27
- package/package.json +25 -21
package/CHANGELOG.md
CHANGED
|
@@ -2,16 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
The format is based on
|
|
6
|
-
|
|
5
|
+
The format is based on
|
|
6
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this
|
|
7
|
+
project adheres to
|
|
8
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
9
|
|
|
8
10
|
## [Unreleased]
|
|
9
11
|
|
|
12
|
+
## [1.3.0] - 2026-07-19
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- TypeScript: infer alias property names from static path-chart string
|
|
17
|
+
literals and `as const` line arrays (e.g. `paths.TEMPLATES`
|
|
18
|
+
type-checks; unknown aliases error)
|
|
19
|
+
|
|
20
|
+
- TypeScript: declare `zip`, default export, `TPathChart`,
|
|
21
|
+
`TZipOptions`, and related helper types in `index.d.ts`
|
|
22
|
+
|
|
23
|
+
- Tests: broader API coverage (zip modes, array input, comments, CRLF,
|
|
24
|
+
URL schemes); `test:types` and `test:coverage` scripts
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- `//` comment stripping no longer treats the `//` in URL schemes (e.g.
|
|
29
|
+
`https://example.com`) as a comment
|
|
30
|
+
|
|
31
|
+
- Full-line `//` comments are dropped cleanly (no leftover empty
|
|
32
|
+
segments / `.` path pieces)
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- `package.json` `exports` now include a `types` condition; `main`
|
|
37
|
+
points at `dist/cjs/…`
|
|
38
|
+
|
|
39
|
+
- Document Node.js `>=20` as the supported engine (dev tooling and
|
|
40
|
+
`node:test`)
|
|
41
|
+
|
|
42
|
+
- Tooling: native `node:test` instead of Jest; ESLint 10 flat config;
|
|
43
|
+
dependency upgrades (`url-join` 5, TypeScript 7 for type tests, etc.)
|
|
44
|
+
|
|
45
|
+
- README: note TypeScript alias inference for static charts
|
|
46
|
+
|
|
47
|
+
## [1.2.3] - 2023-06-06
|
|
48
|
+
|
|
49
|
+
### Updated
|
|
50
|
+
|
|
51
|
+
- README tweaks
|
|
52
|
+
- Update build dependencies
|
|
53
|
+
|
|
10
54
|
## [1.2.2] - 2022-04-15
|
|
11
55
|
|
|
12
56
|
### Removed / Fixed
|
|
13
57
|
|
|
14
|
-
- Remove `browser` setting from `package.json`; seems to cause issues
|
|
58
|
+
- Remove `browser` setting from `package.json`; seems to cause issues
|
|
59
|
+
with CodeSandbox build process, and isn't really necessary since the
|
|
60
|
+
browser-build is something that's interacted with manually.
|
|
15
61
|
|
|
16
62
|
## [1.2.1] - 2022-02-07
|
|
17
63
|
|
|
@@ -47,7 +93,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
47
93
|
|
|
48
94
|
### Fixed
|
|
49
95
|
|
|
50
|
-
- Fix ESM build (import { ... } from 'compose-paths' was not working
|
|
96
|
+
- Fix ESM build (import { ... } from 'compose-paths' was not working
|
|
97
|
+
right)
|
|
51
98
|
|
|
52
99
|
## [1.0.8] - 2021-06-04
|
|
53
100
|
|
package/README.md
CHANGED
|
@@ -18,15 +18,7 @@
|
|
|
18
18
|
/></a>
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
$ pnpm i compose-paths
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
`compose-paths` looks at the indentation-level of its input as the cue to concatenate lines together. Either `tabs` or `spaces` should be fine, so long as you're consistent.
|
|
26
|
-
|
|
27
|
-
## Usage
|
|
28
|
-
|
|
29
|
-
### `Path` → `Alias`
|
|
21
|
+
Quickly throw together a few local paths and have them assigned to aliases or routes.
|
|
30
22
|
|
|
31
23
|
```js
|
|
32
24
|
const { composePaths } = require('compose-paths')
|
|
@@ -54,7 +46,18 @@ paths.aliases
|
|
|
54
46
|
// ["TEMPLATES", "PAGES", "PUBLIC", "IMAGES"]
|
|
55
47
|
```
|
|
56
48
|
|
|
57
|
-
|
|
49
|
+
`compose-paths` looks at the indentation-level of its input as the cue to
|
|
50
|
+
concatenate lines together. Either `tabs` or `spaces` should be fine, so long
|
|
51
|
+
as you're consistent.
|
|
52
|
+
|
|
53
|
+
**TypeScript:** when the path chart is a string literal or template (or an `as
|
|
54
|
+
const` array of lines), alias names after `=` are inferred on the result — so
|
|
55
|
+
`paths.TEMPLATES` type-checks and unknown aliases are errors. Dynamic `string`
|
|
56
|
+
inputs stay loosely typed.
|
|
57
|
+
|
|
58
|
+
## More examples
|
|
59
|
+
|
|
60
|
+
### `Path` → `Route` (via aliases and `zip`)
|
|
58
61
|
|
|
59
62
|
```js
|
|
60
63
|
const { composePaths, zip } = require('compose-paths')
|
|
@@ -76,7 +79,6 @@ const paths = composePaths(`
|
|
|
76
79
|
|
|
77
80
|
const staticRoutes = zip(routes, paths)
|
|
78
81
|
|
|
79
|
-
// Then, for example:
|
|
80
82
|
staticRoutes.forEach(([route, path]) => {
|
|
81
83
|
app.get(route, sendFile(path))
|
|
82
84
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* compose-paths
|
|
3
|
-
* v1.
|
|
3
|
+
* v1.3.0
|
|
4
4
|
* https://github.com/shuckster/compose-paths
|
|
5
5
|
* License: MIT
|
|
6
6
|
*/
|
|
7
|
-
var composePaths=(()=>{var
|
|
7
|
+
var composePaths=(()=>{var r=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var j=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(s){throw n=[s],s}};var P=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(n){throw t=0,n}},S=(e,t)=>{for(var n in t)r(e,n,{get:t[n],enumerable:!0})},$=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of v(t))!I.call(e,i)&&i!==n&&r(e,i,{get:()=>t[i],enumerable:!(s=m(t,i))||s.enumerable});return e};var b=e=>$(r({},"__esModule",{value:!0}),e);var p={};S(p,{default:()=>f});function E(e){var t=[];if(e.length===0)return"";if(typeof e[0]!="string")throw new TypeError("Url must be a string. Received "+e[0]);if(e[0].match(/^[^/:]+:\/*$/)&&e.length>1){var n=e.shift();e[0]=n+e[0]}e[0].match(/^file:\/\/\//)?e[0]=e[0].replace(/^([^/:]+):\/*/,"$1:///"):e[0]=e[0].replace(/^([^/:]+):\/*/,"$1://");for(var s=0;s<e.length;s++){var i=e[s];if(typeof i!="string")throw new TypeError("Url must be a string. Received "+i);i!==""&&(s>0&&(i=i.replace(/^[\/]+/,"")),s<e.length-1?i=i.replace(/[\/]+$/,""):i=i.replace(/[\/]+$/,"/"),t.push(i))}var l=t.join("/");l=l.replace(/\/(\?|&|#[^!])/g,"$1");var o=l.split("?");return l=o.shift()+(o.length>0?"?":"")+o.join("&"),l}function f(){var e;return typeof arguments[0]=="object"?e=arguments[0]:e=[].slice.call(arguments),E(e)}var h=j(()=>{});var D=P((G,g)=>{g.exports={composePaths:w,zip:x};var R=(h(),b(p)).default,d="aliases";function w(e){let n=W(e).map(F),s=n.reduce(C,1/0),i=n.map(J(s)),l=U(i),o=T(l),c=l.map(u=>u==null?void 0:u.name).filter(Boolean),a=o;return l.forEach(N(a)),Object.defineProperty(a,d,{value:c,enumerable:!1})}function x(e,t,n){let{aliases:s=[],ignoreAliases:i=!1}=n||{};return(s.length&&!i?s:e[d]).reduce((o,c)=>(i&&s.includes(c)||o.push([e[c],t[c]]),o),[])}var _=/[\r\n]/,L=/(^\s*\/\/[^\n\r]*|\s+\/\/[^\n\r]*)/,O=/^(\s*)([^$]*)/,z=/\s*=\s*([^$]+)/,B=/^\s*$/;function C(e,{indent:t}){return Math.min(e,t)}function J(e){return({indent:t,content:n})=>({indent:t-e,content:n})}function N(e){return({name:t,index:n})=>{if(!t)return e;let s=e[n];Object.defineProperty(e,t,{value:s,enumerable:!1})}}function T(e){let t=[],n=[],s=-1,i=-1;return e.forEach(({indent:l,content:o})=>{if(i<=0)i=l;else if(l<=s){let c=1+(s-l)/i;for(;c--;)t.pop()}t.push(o),n.push(R(...t)),s=l}),n}function U(e){return e.map((t,n)=>{let{content:s,indent:i}=t,l=s.match(z);return l?{index:n,indent:i,content:s.slice(0,s.length-l[0].length),name:l[1]}:{index:n,indent:i,content:s}})}function F(e){var s;let t=[e,"",e],n=(s=e.match(O))!=null?s:t;return{indent:n[1].length,content:n[2]}}function W(e){return[e].flat().reduce((t,n)=>[...t,...n.split(_)],[]).map(t=>t.replace(L,"")).filter(q)}function q(e){return!B.test(e)}});return D();})();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* compose-paths
|
|
3
|
-
* v1.
|
|
3
|
+
* v1.3.0
|
|
4
4
|
* https://github.com/shuckster/compose-paths
|
|
5
5
|
* License: MIT
|
|
6
6
|
*/
|
|
7
|
-
module.exports={composePaths:
|
|
7
|
+
module.exports={composePaths:p,zip:h};const f=require("path").join,a="aliases";function p(n){const e=x(n).map(v),s=e.reduce(S,1/0),i=e.map(g(s)),o=b(i),r=_(o),c=o.map(u=>u==null?void 0:u.name).filter(Boolean),l=r;return o.forEach(j(l)),Object.defineProperty(l,a,{value:c,enumerable:!1})}function h(n,t,e){const{aliases:s=[],ignoreAliases:i=!1}=e||{};return(s.length&&!i?s:n[a]).reduce((r,c)=>(i&&s.includes(c)||r.push([n[c],t[c]]),r),[])}const d=/[\r\n]/,m=/(^\s*\/\/[^\n\r]*|\s+\/\/[^\n\r]*)/,I=/^(\s*)([^$]*)/,A=/\s*=\s*([^$]+)/,P=/^\s*$/;function S(n,{indent:t}){return Math.min(n,t)}function g(n){return({indent:t,content:e})=>({indent:t-n,content:e})}function j(n){return({name:t,index:e})=>{if(!t)return n;const s=n[e];Object.defineProperty(n,t,{value:s,enumerable:!1})}}function _(n){const t=[],e=[];let s=-1,i=-1;return n.forEach(({indent:o,content:r})=>{if(i<=0)i=o;else if(o<=s){let c=1+(s-o)/i;for(;c--;)t.pop()}t.push(r),e.push(f(...t)),s=o}),e}function b(n){return n.map((t,e)=>{const{content:s,indent:i}=t,o=s.match(A);return o?{index:e,indent:i,content:s.slice(0,s.length-o[0].length),name:o[1]}:{index:e,indent:i,content:s}})}function v(n){var s;const t=[n,"",n],e=(s=n.match(I))!=null?s:t;return{indent:e[1].length,content:e[2]}}function x(n){return[n].flat().reduce((t,e)=>[...t,...e.split(d)],[]).map(t=>t.replace(m,"")).filter(E)}function E(n){return!P.test(n)}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* compose-paths
|
|
3
|
-
* v1.
|
|
3
|
+
* v1.3.0
|
|
4
4
|
* https://github.com/shuckster/compose-paths
|
|
5
5
|
* License: MIT
|
|
6
6
|
*/
|
|
7
|
-
var
|
|
7
|
+
var r=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var j=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(s){throw n=[s],s}};var P=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(n){throw t=0,n}},S=(e,t)=>{for(var n in t)r(e,n,{get:t[n],enumerable:!0})},$=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of v(t))!I.call(e,i)&&i!==n&&r(e,i,{get:()=>t[i],enumerable:!(s=m(t,i))||s.enumerable});return e};var b=e=>$(r({},"__esModule",{value:!0}),e);var p={};S(p,{default:()=>f});function E(e){var t=[];if(e.length===0)return"";if(typeof e[0]!="string")throw new TypeError("Url must be a string. Received "+e[0]);if(e[0].match(/^[^/:]+:\/*$/)&&e.length>1){var n=e.shift();e[0]=n+e[0]}e[0].match(/^file:\/\/\//)?e[0]=e[0].replace(/^([^/:]+):\/*/,"$1:///"):e[0]=e[0].replace(/^([^/:]+):\/*/,"$1://");for(var s=0;s<e.length;s++){var i=e[s];if(typeof i!="string")throw new TypeError("Url must be a string. Received "+i);i!==""&&(s>0&&(i=i.replace(/^[\/]+/,"")),s<e.length-1?i=i.replace(/[\/]+$/,""):i=i.replace(/[\/]+$/,"/"),t.push(i))}var l=t.join("/");l=l.replace(/\/(\?|&|#[^!])/g,"$1");var o=l.split("?");return l=o.shift()+(o.length>0?"?":"")+o.join("&"),l}function f(){var e;return typeof arguments[0]=="object"?e=arguments[0]:e=[].slice.call(arguments),E(e)}var h=j(()=>{});var D=P((G,g)=>{g.exports={composePaths:w,zip:x};var R=(h(),b(p)).default,d="aliases";function w(e){let n=W(e).map(F),s=n.reduce(C,1/0),i=n.map(J(s)),l=U(i),o=T(l),c=l.map(u=>u==null?void 0:u.name).filter(Boolean),a=o;return l.forEach(N(a)),Object.defineProperty(a,d,{value:c,enumerable:!1})}function x(e,t,n){let{aliases:s=[],ignoreAliases:i=!1}=n||{};return(s.length&&!i?s:e[d]).reduce((o,c)=>(i&&s.includes(c)||o.push([e[c],t[c]]),o),[])}var _=/[\r\n]/,L=/(^\s*\/\/[^\n\r]*|\s+\/\/[^\n\r]*)/,O=/^(\s*)([^$]*)/,z=/\s*=\s*([^$]+)/,B=/^\s*$/;function C(e,{indent:t}){return Math.min(e,t)}function J(e){return({indent:t,content:n})=>({indent:t-e,content:n})}function N(e){return({name:t,index:n})=>{if(!t)return e;let s=e[n];Object.defineProperty(e,t,{value:s,enumerable:!1})}}function T(e){let t=[],n=[],s=-1,i=-1;return e.forEach(({indent:l,content:o})=>{if(i<=0)i=l;else if(l<=s){let c=1+(s-l)/i;for(;c--;)t.pop()}t.push(o),n.push(R(...t)),s=l}),n}function U(e){return e.map((t,n)=>{let{content:s,indent:i}=t,l=s.match(z);return l?{index:n,indent:i,content:s.slice(0,s.length-l[0].length),name:l[1]}:{index:n,indent:i,content:s}})}function F(e){var s;let t=[e,"",e],n=(s=e.match(O))!=null?s:t;return{indent:n[1].length,content:n[2]}}function W(e){return[e].flat().reduce((t,n)=>[...t,...n.split(_)],[]).map(t=>t.replace(L,"")).filter(q)}function q(e){return!B.test(e)}});export default D();
|
package/index.d.ts
CHANGED
|
@@ -1,32 +1,205 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Public path-chart types
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
export type TPathChart = string | readonly string[]
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Every composed path string, in chart order.
|
|
9
|
+
*
|
|
10
|
+
* Also has non-enumerable `.aliases` and one non-enumerable property per alias
|
|
11
|
+
* (e.g. `paths.TEMPLATES`). With a static chart string, those alias keys are
|
|
12
|
+
* inferred by TypeScript (see `composePaths` overloads).
|
|
13
|
+
*/
|
|
14
|
+
export interface TComposedPaths extends Array<string> {
|
|
15
|
+
/** Alias names in declaration order (non-enumerable at runtime). */
|
|
16
|
+
readonly aliases: readonly string[]
|
|
4
17
|
}
|
|
5
18
|
|
|
6
|
-
|
|
19
|
+
export interface TZipOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Alias names to include. If `ignoreAliases` is true, these are excluded
|
|
22
|
+
* instead and every other alias on `keys` is kept.
|
|
23
|
+
*/
|
|
24
|
+
aliases?: readonly string[]
|
|
7
25
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* ${__dirname}
|
|
12
|
-
* /src
|
|
13
|
-
* /html
|
|
14
|
-
* /templates = TEMPLATES
|
|
15
|
-
* /pages = PAGES
|
|
16
|
-
*
|
|
17
|
-
* /public = PUBLIC
|
|
18
|
-
* /images = IMAGES
|
|
19
|
-
*
|
|
20
|
-
* `)
|
|
21
|
-
*
|
|
22
|
-
* paths.TEMPLATES
|
|
23
|
-
* // "/dir/name/src/html/templates"
|
|
24
|
-
*
|
|
25
|
-
* paths.PUBLIC
|
|
26
|
-
* // "/dir/name/public"
|
|
27
|
-
*
|
|
28
|
-
* paths.aliases
|
|
29
|
-
* // ["TEMPLATES", "PAGES", "PUBLIC", "IMAGES"]
|
|
26
|
+
* When true, `aliases` is treated as an exclusion list.
|
|
27
|
+
* @default false
|
|
30
28
|
*/
|
|
31
|
-
|
|
29
|
+
ignoreAliases?: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Type-level chart DSL (alias-name extraction only)
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
type TTrimLeft<S extends string> = S extends ` ${infer R}`
|
|
37
|
+
? TTrimLeft<R>
|
|
38
|
+
: S extends `\t${infer R}`
|
|
39
|
+
? TTrimLeft<R>
|
|
40
|
+
: S
|
|
41
|
+
|
|
42
|
+
type TTrimRight<S extends string> = S extends `${infer R} `
|
|
43
|
+
? TTrimRight<R>
|
|
44
|
+
: S extends `${infer R}\t`
|
|
45
|
+
? TTrimRight<R>
|
|
46
|
+
: S
|
|
47
|
+
|
|
48
|
+
type TTrim<S extends string> = TTrimLeft<TTrimRight<S>>
|
|
49
|
+
|
|
50
|
+
/** Normalize CRLF so line splitting is `\n`-only. */
|
|
51
|
+
type TNormalizeNewlines<S extends string> = S extends `${infer A}\r\n${infer B}`
|
|
52
|
+
? `${A}\n${TNormalizeNewlines<B>}`
|
|
53
|
+
: S
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Strip `//` comments the same way as runtime: at line start (optional indent)
|
|
57
|
+
* or after whitespace — not the `//` inside `https://…`.
|
|
58
|
+
*/
|
|
59
|
+
type TStripLineComment<S extends string> = TTrimLeft<S> extends `//${string}`
|
|
60
|
+
? ''
|
|
61
|
+
: S extends `${infer Code} //${string}`
|
|
62
|
+
? Code
|
|
63
|
+
: S extends `${infer Code}\t//${string}`
|
|
64
|
+
? Code
|
|
65
|
+
: S
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Split on newlines. Depth is unbounded in theory; real path charts stay small.
|
|
69
|
+
* Empty segments from trailing newlines become `never` aliases and drop out.
|
|
70
|
+
*/
|
|
71
|
+
type TSplitLines<S extends string> = TNormalizeNewlines<S> extends `${infer Line}\n${infer Rest}`
|
|
72
|
+
? [Line, ...TSplitLines<Rest>]
|
|
73
|
+
: [TNormalizeNewlines<S>]
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Alias text after `=` on a chart line, or `never` if the line has no assignment.
|
|
77
|
+
* Distributes over unions of lines (`L extends unknown ? …`) so chart-wide
|
|
78
|
+
* `TSplitLines[number]` extracts each line’s alias correctly.
|
|
79
|
+
*/
|
|
80
|
+
export type TAliasFromLine<L extends string> = L extends unknown
|
|
81
|
+
? TStripLineComment<L> extends `${string}=${infer Alias}`
|
|
82
|
+
? TTrim<TStripLineComment<Alias>> extends infer A extends string
|
|
83
|
+
? A extends ''
|
|
84
|
+
? never
|
|
85
|
+
: A
|
|
86
|
+
: never
|
|
87
|
+
: never
|
|
88
|
+
: never
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Union of alias names in a static chart string.
|
|
92
|
+
* Dynamic `string` inputs fall back to `string`.
|
|
93
|
+
*/
|
|
94
|
+
export type TAliasesFromChart<S extends string> = string extends S
|
|
95
|
+
? string
|
|
96
|
+
: TAliasFromLine<TSplitLines<S>[number]>
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Alias names from a `as const` array of chart lines.
|
|
100
|
+
*/
|
|
101
|
+
export type TAliasesFromLines<T extends readonly string[]> = string extends T[number]
|
|
102
|
+
? string
|
|
103
|
+
: TAliasFromLine<T[number]>
|
|
104
|
+
|
|
105
|
+
type TAliasPropsFromUnion<A extends string> = string extends A
|
|
106
|
+
? { readonly [key: string]: string }
|
|
107
|
+
: { readonly [K in A]: string }
|
|
108
|
+
|
|
109
|
+
export type TAliasPropsFromChart<S extends string> = TAliasPropsFromUnion<
|
|
110
|
+
TAliasesFromChart<S>
|
|
111
|
+
>
|
|
112
|
+
|
|
113
|
+
export type TAliasPropsFromLines<T extends readonly string[]> = TAliasPropsFromUnion<
|
|
114
|
+
TAliasesFromLines<T>
|
|
115
|
+
>
|
|
116
|
+
|
|
117
|
+
export type TComposedPathsFromChart<S extends string> = TComposedPaths &
|
|
118
|
+
TAliasPropsFromChart<S> & {
|
|
119
|
+
readonly aliases: ReadonlyArray<TAliasesFromChart<S>>
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export type TComposedPathsFromLines<T extends readonly string[]> = TComposedPaths &
|
|
123
|
+
TAliasPropsFromLines<T> & {
|
|
124
|
+
readonly aliases: ReadonlyArray<TAliasesFromLines<T>>
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// composePaths
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Compose indented path charts into a list of full paths + named aliases.
|
|
133
|
+
*
|
|
134
|
+
* When `pathChart` is a string literal / template, TypeScript infers alias
|
|
135
|
+
* property names from `= ALIAS` markers in the chart.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* const paths = composePaths(`
|
|
139
|
+
* ${__dirname}
|
|
140
|
+
* /src
|
|
141
|
+
* /html
|
|
142
|
+
* /templates = TEMPLATES
|
|
143
|
+
* /pages = PAGES
|
|
144
|
+
*
|
|
145
|
+
* /public = PUBLIC
|
|
146
|
+
* /images = IMAGES
|
|
147
|
+
* `)
|
|
148
|
+
*
|
|
149
|
+
* paths.TEMPLATES
|
|
150
|
+
* // "/dir/name/src/html/templates"
|
|
151
|
+
*
|
|
152
|
+
* paths.PUBLIC
|
|
153
|
+
* // "/dir/name/public"
|
|
154
|
+
*
|
|
155
|
+
* paths.aliases
|
|
156
|
+
* // ["TEMPLATES", "PAGES", "PUBLIC", "IMAGES"]
|
|
157
|
+
*/
|
|
158
|
+
export function composePaths<const S extends string>(
|
|
159
|
+
pathChart: S
|
|
160
|
+
): TComposedPathsFromChart<S>
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Compose from an `as const` array of chart lines (alias keys inferred).
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* composePaths(['/src', ' /x = X'] as const).X
|
|
167
|
+
*/
|
|
168
|
+
export function composePaths<const T extends readonly string[]>(
|
|
169
|
+
pathChart: T
|
|
170
|
+
): TComposedPathsFromLines<T>
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Compose when the chart type is a wide `string` or `string[]` (no key inference).
|
|
174
|
+
*/
|
|
175
|
+
export function composePaths(pathChart: TPathChart): TComposedPaths
|
|
176
|
+
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
// zip
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Pair aliases from two composed charts (e.g. routes → filesystem paths).
|
|
183
|
+
*
|
|
184
|
+
* @returns `[keys[alias], values[alias]]` tuples in alias order
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* zip(routes, paths)
|
|
188
|
+
* zip(routes, paths, { aliases: ['HOME', 'ABOUT'] })
|
|
189
|
+
* zip(routes, paths, { aliases: ['DEBUG'], ignoreAliases: true })
|
|
190
|
+
*/
|
|
191
|
+
export function zip<K extends TComposedPaths, V extends TComposedPaths>(
|
|
192
|
+
keys: K,
|
|
193
|
+
values: V,
|
|
194
|
+
options?: TZipOptions
|
|
195
|
+
): Array<[string, string]>
|
|
196
|
+
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
// Default export (CJS/ESM interop shape)
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
|
|
201
|
+
declare const _default: {
|
|
202
|
+
composePaths: typeof composePaths
|
|
203
|
+
zip: typeof zip
|
|
32
204
|
}
|
|
205
|
+
export default _default
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "compose-paths",
|
|
3
3
|
"browserGlobalName": "composePaths",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.3.0",
|
|
5
5
|
"description": "Define paths + routes without the repetition.",
|
|
6
6
|
"author": "Conan Theobald",
|
|
7
7
|
"license": "MIT",
|
|
@@ -12,13 +12,16 @@
|
|
|
12
12
|
"routes",
|
|
13
13
|
"concatenation"
|
|
14
14
|
],
|
|
15
|
-
"types": "index.d.ts",
|
|
16
|
-
"main": "./dist/compose-paths.cjs.js",
|
|
15
|
+
"types": "./index.d.ts",
|
|
16
|
+
"main": "./dist/cjs/compose-paths.cjs.js",
|
|
17
17
|
"module": "index.mjs",
|
|
18
18
|
"exports": {
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./index.d.ts",
|
|
21
|
+
"import": "./index.mjs",
|
|
22
|
+
"require": "./dist/cjs/compose-paths.cjs.js",
|
|
23
|
+
"default": "./dist/cjs/compose-paths.cjs.js"
|
|
24
|
+
}
|
|
22
25
|
},
|
|
23
26
|
"files": [
|
|
24
27
|
"dist/**/*.js",
|
|
@@ -30,7 +33,7 @@
|
|
|
30
33
|
"LICENSE"
|
|
31
34
|
],
|
|
32
35
|
"engines": {
|
|
33
|
-
"node": ">=
|
|
36
|
+
"node": ">=20"
|
|
34
37
|
},
|
|
35
38
|
"homepage": "https://github.com/shuckster/compose-paths",
|
|
36
39
|
"repository": {
|
|
@@ -42,24 +45,25 @@
|
|
|
42
45
|
"email": "bugs+compose-paths@conans.co.uk"
|
|
43
46
|
},
|
|
44
47
|
"devDependencies": {
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"eslint
|
|
50
|
-
"
|
|
51
|
-
"match-iz": "^
|
|
52
|
-
"rimraf": "^
|
|
53
|
-
"typescript": "^
|
|
54
|
-
"url-join": "^
|
|
48
|
+
"@eslint/js": "^10.0.1",
|
|
49
|
+
"concurrently": "^10.0.3",
|
|
50
|
+
"cross-env": "^10.1.0",
|
|
51
|
+
"esbuild": "^0.28.1",
|
|
52
|
+
"eslint": "^10.7.0",
|
|
53
|
+
"globals": "^17.7.0",
|
|
54
|
+
"match-iz": "^5.0.8",
|
|
55
|
+
"rimraf": "^6.1.3",
|
|
56
|
+
"typescript": "^7.0.2",
|
|
57
|
+
"url-join": "^5.0.0"
|
|
55
58
|
},
|
|
56
59
|
"scripts": {
|
|
57
|
-
"test": "
|
|
58
|
-
"
|
|
60
|
+
"test": "node --test tests/*.test.js",
|
|
61
|
+
"test:coverage": "node --test --experimental-test-coverage --test-coverage-include='src/**' tests/*.test.js",
|
|
62
|
+
"test:types": "tsc --strict --noEmit --module NodeNext --moduleResolution NodeNext tests/types-fixture.ts",
|
|
63
|
+
"lint": "eslint src/*.js tests/*.js build-config/*.js eslint.config.js",
|
|
59
64
|
"build": "cross-env NODE_ENV=production pnpm run build:esbuild",
|
|
60
65
|
"build:clean": "rimraf dist/; mkdir -p dist/{esm,cjs,browser}",
|
|
61
66
|
"build:all": "pnpm run build:clean ; cross-env NODE_ENV=production pnpm run build",
|
|
62
67
|
"build:esbuild": "node ./build-config/esbuild.js"
|
|
63
|
-
}
|
|
64
|
-
"readme": "<h1 align=\"center\"><code>compose-paths</code> 🛣</h1>\n\n<p align=\"center\">\n <a href=\"https://github.com/shuckster/compose-paths/blob/master/LICENSE\">\n <img\n alt=\"MIT license\"\n src=\"https://img.shields.io/npm/l/compose-paths?style=plastic\"\n /></a>\n <a href=\"https://bundlephobia.com/result?p=compose-paths\">\n <img\n alt=\"npm bundle size\"\n src=\"https://img.shields.io/bundlephobia/minzip/compose-paths?style=plastic\"\n /></a>\n <a href=\"https://www.npmjs.com/package/compose-paths\">\n <img\n alt=\"Version\"\n src=\"https://img.shields.io/npm/v/compose-paths?style=plastic\"\n /></a>\n</p>\n\n```sh\n$ pnpm i compose-paths\n```\n\n`compose-paths` looks at the indentation-level of its input as the cue to concatenate lines together. Either `tabs` or `spaces` should be fine, so long as you're consistent.\n\n## Usage\n\n### `Path` → `Alias`\n\n```js\nconst { composePaths } = require('compose-paths')\n\nconst paths = composePaths(`\n\n ${__dirname}\n /src\n /html\n /templates = TEMPLATES\n /pages = PAGES\n\n /public = PUBLIC\n /images = IMAGES\n\n`)\n\npaths.TEMPLATES\n// \"/dir/name/src/html/templates\"\n\npaths.PUBLIC\n// \"/dir/name/public\"\n\npaths.aliases\n// [\"TEMPLATES\", \"PAGES\", \"PUBLIC\", \"IMAGES\"]\n```\n\n### `Path` → `Route` (via aliases)\n\n```js\nconst { composePaths, zip } = require('compose-paths')\n\nconst routes = composePaths(`\n / = HOME\n /about = ABOUT\n /contact = CONTACT\n`)\n\nconst paths = composePaths(`\n ${__dirname}\n /src\n /html/pages\n /index.html = HOME\n /about.html = ABOUT\n /contact.html = CONTACT\n`)\n\nconst staticRoutes = zip(routes, paths)\n\n// Then, for example:\nstaticRoutes.forEach(([route, path]) => {\n app.get(route, sendFile(path))\n})\n```\n\n### `Path` → `Route` (directly)\n\n```js\nconst { composePaths } = require('compose-paths')\n\nconst pathFromRoute = composePaths(`\n\n ${__dirname}\n /src/html/pages\n /index.html = /\n /about.html = /about\n /contact.html = /contact\n\n`)\n\npathFromRoute['/']\n// \"/dir/name/src/html/pages/index.html\"\n\npathFromRoute['/contact']\n// \"/dir/name/src/html/pages/contact.html\"\n\npathFromRoute.aliases.forEach(route => {\n console.log(pathFromRoute[route])\n})\n```\n\nThat's it!\n\n# Credits\n\n`compose-paths` was written by [Conan Theobald](https://github.com/shuckster/).\n\nDid you find this useful? If so, I like [coffee ☕️](https://www.buymeacoffee.com/shuckster) :)\n\n## License\n\nMIT licensed: See [LICENSE](LICENSE)\n"
|
|
68
|
+
}
|
|
65
69
|
}
|