@flex-development/mlly 1.0.0-alpha.11 → 1.0.0-alpha.13
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 +101 -0
- package/README.md +1 -1
- package/dist/interfaces/options-get-format.d.mts +3 -13
- package/dist/interfaces/options-get-source.d.mts +3 -13
- package/dist/interfaces/options-parse-subpath.d.mts +18 -0
- package/dist/interfaces/parsed-subpath.d.mts +5 -1
- package/dist/internal/regex-invalid-segment.d.mts +14 -0
- package/dist/internal/regex-invalid-segment.mjs +11 -0
- package/dist/internal/regex-invalid-segment.mjs.map +6 -0
- package/dist/internal/resolver.mjs +6 -9
- package/dist/internal/resolver.mjs.map +1 -1
- package/dist/internal/validate-array-set.d.mts +22 -0
- package/dist/internal/validate-array-set.mjs +11 -0
- package/dist/internal/validate-array-set.mjs.map +6 -0
- package/dist/internal/validate-map.d.mts +23 -0
- package/dist/internal/validate-map.mjs +11 -0
- package/dist/internal/validate-map.mjs.map +6 -0
- package/dist/internal/validate-set.d.mts +4 -5
- package/dist/internal/validate-set.mjs.map +1 -1
- package/dist/utils/fill-modules.d.mts +1 -0
- package/dist/utils/fill-modules.mjs +7 -1
- package/dist/utils/fill-modules.mjs.map +1 -1
- package/dist/utils/find-requires.mjs +1 -1
- package/dist/utils/find-subpath.d.mts +2 -1
- package/dist/utils/find-subpath.mjs +2 -0
- package/dist/utils/find-subpath.mjs.map +1 -1
- package/dist/utils/get-format.d.mts +8 -1
- package/dist/utils/get-format.mjs +22 -11
- package/dist/utils/get-format.mjs.map +1 -1
- package/dist/utils/get-source.d.mts +7 -0
- package/dist/utils/get-source.mjs +13 -6
- package/dist/utils/get-source.mjs.map +1 -1
- package/dist/utils/is-absolute-specifier.d.mts +1 -0
- package/dist/utils/is-absolute-specifier.mjs +2 -0
- package/dist/utils/is-absolute-specifier.mjs.map +1 -1
- package/dist/utils/is-bare-specifier.d.mts +1 -0
- package/dist/utils/is-bare-specifier.mjs +2 -0
- package/dist/utils/is-bare-specifier.mjs.map +1 -1
- package/dist/utils/is-exports-sugar.mjs +1 -1
- package/dist/utils/is-exports-sugar.mjs.map +1 -1
- package/dist/utils/is-relative-specifier.mjs.map +1 -1
- package/dist/utils/parse-module-id.d.mts +1 -3
- package/dist/utils/parse-module-id.mjs +3 -0
- package/dist/utils/parse-module-id.mjs.map +1 -1
- package/dist/utils/parse-subpath.d.mts +1 -2
- package/dist/utils/parse-subpath.mjs +115 -5
- package/dist/utils/parse-subpath.mjs.map +1 -1
- package/dist/utils/read-package-json.mjs +7 -5
- package/dist/utils/read-package-json.mjs.map +1 -1
- package/dist/utils/resolve-alias.mjs +3 -2
- package/dist/utils/resolve-alias.mjs.map +1 -1
- package/dist/utils/resolve-module.mjs +16 -1
- package/dist/utils/resolve-module.mjs.map +1 -1
- package/dist/utils/to-bare-specifier.mjs +8 -10
- package/dist/utils/to-bare-specifier.mjs.map +1 -1
- package/dist/utils/to-relative-specifier.d.mts +2 -0
- package/dist/utils/to-relative-specifier.mjs +3 -0
- package/dist/utils/to-relative-specifier.mjs.map +1 -1
- package/dist/utils/to-url.d.mts +2 -3
- package/dist/utils/to-url.mjs.map +1 -1
- package/dist/utils/validate-assertions.mjs +2 -0
- package/dist/utils/validate-assertions.mjs.map +1 -1
- package/dist/utils/validate-exports.d.mts +2 -2
- package/dist/utils/validate-exports.mjs +2 -2
- package/dist/utils/validate-exports.mjs.map +1 -1
- package/package.json +34 -41
- package/src/interfaces/options-get-format.ts +2 -14
- package/src/interfaces/options-get-source.ts +2 -14
- package/src/interfaces/options-parse-subpath.ts +20 -0
- package/src/interfaces/parsed-subpath.ts +10 -1
- package/src/internal/regex-invalid-segment.ts +23 -0
- package/src/internal/resolver.ts +7 -22
- package/src/internal/validate-array-set.ts +32 -0
- package/src/internal/validate-map.ts +33 -0
- package/src/internal/validate-set.ts +4 -5
- package/src/utils/fill-modules.ts +10 -1
- package/src/utils/find-requires.ts +1 -1
- package/src/utils/find-subpath.ts +11 -2
- package/src/utils/get-format.ts +33 -12
- package/src/utils/get-source.ts +21 -6
- package/src/utils/is-absolute-specifier.ts +5 -0
- package/src/utils/is-bare-specifier.ts +5 -0
- package/src/utils/is-exports-sugar.ts +1 -1
- package/src/utils/is-relative-specifier.ts +1 -0
- package/src/utils/parse-module-id.ts +6 -3
- package/src/utils/parse-subpath.ts +154 -9
- package/src/utils/read-package-json.ts +8 -3
- package/src/utils/resolve-alias.ts +3 -2
- package/src/utils/resolve-module.ts +40 -11
- package/src/utils/to-bare-specifier.ts +10 -12
- package/src/utils/to-relative-specifier.ts +7 -0
- package/src/utils/to-url.ts +2 -3
- package/src/utils/validate-assertions.ts +2 -0
- package/src/utils/validate-exports.ts +5 -5
- package/changelog.config.ts +0 -404
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Internal - validateMap
|
|
3
|
+
* @module mlly/internal/validateMap
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ERR_INVALID_ARG_TYPE, type NodeError } from '@flex-development/errnode'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Checks if given `value` is a {@linkcode Map}.
|
|
10
|
+
*
|
|
11
|
+
* Throws [`ERR_INVALID_ARG_TYPE`][1] if the `value` is not a {@linkcode Map}.
|
|
12
|
+
*
|
|
13
|
+
* [1]: https://nodejs.org/api/errors.html#err_invalid_arg_value
|
|
14
|
+
*
|
|
15
|
+
* @see {@linkcode ERR_INVALID_ARG_TYPE}
|
|
16
|
+
*
|
|
17
|
+
* @template K - Map key type(s)
|
|
18
|
+
* @template V - Item type(s)
|
|
19
|
+
*
|
|
20
|
+
* @param {unknown} value - Value supplied by user
|
|
21
|
+
* @param {string} name - Name of invalid argument or property
|
|
22
|
+
* @return {value is Map<K, V>} `true` if `value` is a {@linkcode Map}
|
|
23
|
+
* @throws {NodeError<TypeError>} If `value` is not a {@linkcode Map}
|
|
24
|
+
*/
|
|
25
|
+
function validateMap<K extends string = string, V = unknown>(
|
|
26
|
+
value: unknown,
|
|
27
|
+
name: string
|
|
28
|
+
): value is Map<K, V> {
|
|
29
|
+
if (value instanceof Map) return true
|
|
30
|
+
throw new ERR_INVALID_ARG_TYPE(name, ['Map'], value)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default validateMap
|
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
import { ERR_INVALID_ARG_TYPE, type NodeError } from '@flex-development/errnode'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Checks if given `value` is
|
|
9
|
+
* Checks if given `value` is a {@linkcode Set}.
|
|
10
10
|
*
|
|
11
|
-
* Throws [`ERR_INVALID_ARG_TYPE`][1] if the `value` is not a {@linkcode Set}
|
|
12
|
-
* instance.
|
|
11
|
+
* Throws [`ERR_INVALID_ARG_TYPE`][1] if the `value` is not a {@linkcode Set}.
|
|
13
12
|
*
|
|
14
13
|
* [1]: https://nodejs.org/api/errors.html#err_invalid_arg_value
|
|
15
14
|
*
|
|
@@ -19,8 +18,8 @@ import { ERR_INVALID_ARG_TYPE, type NodeError } from '@flex-development/errnode'
|
|
|
19
18
|
*
|
|
20
19
|
* @param {unknown} value - Value supplied by user
|
|
21
20
|
* @param {string} name - Name of invalid argument or property
|
|
22
|
-
* @return {value is Set} `true` if `value` is
|
|
23
|
-
* @throws {NodeError<TypeError>} If `value` is
|
|
21
|
+
* @return {value is Set<T>} `true` if `value` is a {@linkcode Set}
|
|
22
|
+
* @throws {NodeError<TypeError>} If `value` is not a {@linkcode Set}
|
|
24
23
|
*/
|
|
25
24
|
function validateSet<T = unknown>(
|
|
26
25
|
value: unknown,
|
|
@@ -7,7 +7,12 @@ import { SpecifierSyntaxKind } from '#src/enums'
|
|
|
7
7
|
import type { FillModuleOptions } from '#src/interfaces'
|
|
8
8
|
import regexp from '#src/internal/escape-reg-exp'
|
|
9
9
|
import isFunction from '#src/internal/is-function'
|
|
10
|
-
import
|
|
10
|
+
import validateArraySet from '#src/internal/validate-array-set'
|
|
11
|
+
import validateURLString from '#src/internal/validate-url-string'
|
|
12
|
+
import {
|
|
13
|
+
ERR_UNKNOWN_FILE_EXTENSION,
|
|
14
|
+
type NodeError
|
|
15
|
+
} from '@flex-development/errnode'
|
|
11
16
|
import pathe from '@flex-development/pathe'
|
|
12
17
|
import type { URL } from 'node:url'
|
|
13
18
|
import CONDITIONS from './conditions'
|
|
@@ -33,6 +38,7 @@ import toRelativeSpecifier from './to-relative-specifier'
|
|
|
33
38
|
* @param {string} code - Code to evaluate
|
|
34
39
|
* @param {FillModuleOptions} options - Module fill options
|
|
35
40
|
* @return {Promise<string>} `code` with fully specified module specifiers
|
|
41
|
+
* @throws {NodeError<TypeError>}
|
|
36
42
|
*/
|
|
37
43
|
const fillModules = async (
|
|
38
44
|
code: string,
|
|
@@ -40,6 +46,9 @@ const fillModules = async (
|
|
|
40
46
|
): Promise<string> => {
|
|
41
47
|
const { conditions = CONDITIONS, ext, parent = import.meta.url } = options
|
|
42
48
|
|
|
49
|
+
validateArraySet(conditions, 'options.conditions')
|
|
50
|
+
validateURLString(parent, 'options.parent')
|
|
51
|
+
|
|
43
52
|
// ensure specifiers have file extensions
|
|
44
53
|
for (const statement of extractStatements(code)) {
|
|
45
54
|
// do nothing if statement does not have specifier
|
|
@@ -33,7 +33,7 @@ const findRequires = (code: string = ''): RequireStatement[] => {
|
|
|
33
33
|
* @const {RegExp} REQUIRE_REGEX
|
|
34
34
|
*/
|
|
35
35
|
const REQUIRE_REGEX: RegExp =
|
|
36
|
-
/(?<=^|[\s
|
|
36
|
+
/(?<=^|[\s,:;([])\b(?:(?:const\s*|let\s*|var\s*)?(?:(?<=(?:const\s*|let\s*|var\s*))(?<imports>(?:[$_\p{ID_Start}][$\u200C\u200D\p{ID_Continue}]*)|(?:[\w\t\n\r "$'*,./:{}-]+?)))?\s*=?\s*(?<kind>require)\((?<specifier>["']?[\S\t\n\r]+?["']?)\))(?<!(?:\/\/|\*).*)/gu
|
|
37
37
|
|
|
38
38
|
return [...code.matchAll(REQUIRE_REGEX)].map(match => {
|
|
39
39
|
const { 0: code = '', index: start = 0, groups = {} } = match
|
|
@@ -24,7 +24,8 @@ import toURL from './to-url'
|
|
|
24
24
|
* Finds the subpath defined in `context`, a `package.json` [`exports`][1] or
|
|
25
25
|
* [`imports`][2] field, that maps to the given package `target`.
|
|
26
26
|
*
|
|
27
|
-
* Supports extensionless targets
|
|
27
|
+
* Supports extensionless targets and targets without explicit `'/index'` usage.
|
|
28
|
+
* Returns `null` if a subpath is not found.
|
|
28
29
|
*
|
|
29
30
|
* [1]: https://nodejs.org/api/packages.html#exports
|
|
30
31
|
* [2]: https://nodejs.org/api/packages.html#imports
|
|
@@ -181,10 +182,17 @@ const findSubpath = (
|
|
|
181
182
|
/**
|
|
182
183
|
* {@linkcode tar} without file extension.
|
|
183
184
|
*
|
|
184
|
-
* @const {string}
|
|
185
|
+
* @const {string} tar_ne
|
|
185
186
|
*/
|
|
186
187
|
const tar_ne: string = pathe.changeExt(tar, '')
|
|
187
188
|
|
|
189
|
+
/**
|
|
190
|
+
* {@linkcode tar_ne} without `'/index'`.
|
|
191
|
+
*
|
|
192
|
+
* @const {string} tar_ni
|
|
193
|
+
*/
|
|
194
|
+
const tar_ni: string = tar_ne.replace(/\/index$/, '')
|
|
195
|
+
|
|
188
196
|
/**
|
|
189
197
|
* Index of {@linkcode PATTERN_CHARACTER} in {@linkcode tar}.
|
|
190
198
|
*
|
|
@@ -196,6 +204,7 @@ const findSubpath = (
|
|
|
196
204
|
// target is an exactish match
|
|
197
205
|
case target === tar:
|
|
198
206
|
case target === tar_ne:
|
|
207
|
+
case target === tar_ni && tar_ne.endsWith('/index'):
|
|
199
208
|
case pattern === -1 && (target === tar || target === tar_ne):
|
|
200
209
|
subpath = pkgsubpath
|
|
201
210
|
break
|
package/src/utils/get-format.ts
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
import { Format } from '#src/enums'
|
|
7
7
|
import type { GetFormatOptions, PackageScope } from '#src/interfaces'
|
|
8
|
+
import validateBoolean from '#src/internal/validate-boolean'
|
|
9
|
+
import validateMap from '#src/internal/validate-map'
|
|
10
|
+
import validateObject from '#src/internal/validate-object'
|
|
8
11
|
import type { ModuleId } from '#src/types'
|
|
9
12
|
import {
|
|
10
13
|
ERR_UNKNOWN_FILE_EXTENSION,
|
|
@@ -12,7 +15,12 @@ import {
|
|
|
12
15
|
} from '@flex-development/errnode'
|
|
13
16
|
import { isBuiltin } from '@flex-development/is-builtin'
|
|
14
17
|
import pathe, { type Ext } from '@flex-development/pathe'
|
|
15
|
-
import
|
|
18
|
+
import {
|
|
19
|
+
isUndefined,
|
|
20
|
+
type EmptyString,
|
|
21
|
+
type Nilable,
|
|
22
|
+
type Nullable
|
|
23
|
+
} from '@flex-development/tutils'
|
|
16
24
|
import type { URL } from 'node:url'
|
|
17
25
|
import EXTENSION_FORMAT_MAP from './extension-format-map'
|
|
18
26
|
import lookupPackageScope from './lookup-package-scope'
|
|
@@ -22,6 +30,13 @@ import toURL from './to-url'
|
|
|
22
30
|
/**
|
|
23
31
|
* Retrieves a module format for the given module `id`.
|
|
24
32
|
*
|
|
33
|
+
* ::: tip
|
|
34
|
+
* The given module `id` should be absolute (i.e. a [`file:` URL][1] or absolute
|
|
35
|
+
* specifier).
|
|
36
|
+
* :::
|
|
37
|
+
*
|
|
38
|
+
* [1]: https://nodejs.org/api/esm.html#file-urls
|
|
39
|
+
*
|
|
25
40
|
* @see {@linkcode Format}
|
|
26
41
|
* @see {@linkcode GetFormatOptions}
|
|
27
42
|
* @see {@linkcode ModuleId}
|
|
@@ -38,21 +53,27 @@ const getFormat = async (
|
|
|
38
53
|
options: GetFormatOptions = {}
|
|
39
54
|
): Promise<Nilable<Format>> => {
|
|
40
55
|
const {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
experimental_wasm_modules = false,
|
|
56
|
+
experimental_json_modules: json_modules = true,
|
|
57
|
+
experimental_network_imports: network_imports = false,
|
|
58
|
+
experimental_wasm_modules: wasm_modules = false,
|
|
45
59
|
extension_format_map = EXTENSION_FORMAT_MAP,
|
|
46
60
|
ignore_errors = false,
|
|
47
|
-
req
|
|
61
|
+
req = {}
|
|
48
62
|
} = options
|
|
49
63
|
|
|
64
|
+
validateBoolean(json_modules, 'options.experimental_json_modules')
|
|
65
|
+
validateBoolean(network_imports, 'options.experimental_network_imports')
|
|
66
|
+
validateBoolean(wasm_modules, 'options.experimental_wasm_modules')
|
|
67
|
+
validateBoolean(ignore_errors, 'options.ignore_errors')
|
|
68
|
+
validateMap(extension_format_map, 'options.extension_format_map')
|
|
69
|
+
!isUndefined(req) && validateObject(req, 'options.req')
|
|
70
|
+
|
|
50
71
|
/**
|
|
51
72
|
* Module {@linkcode id} as {@linkcode URL}.
|
|
52
73
|
*
|
|
53
74
|
* @const {URL} url
|
|
54
75
|
*/
|
|
55
|
-
const url: URL = toURL(id
|
|
76
|
+
const url: URL = toURL(id)
|
|
56
77
|
|
|
57
78
|
/**
|
|
58
79
|
* Extracts a [MIME type][1] from a {@linkcode URL} href, {@linkcode URL}
|
|
@@ -94,10 +115,10 @@ const getFormat = async (
|
|
|
94
115
|
format = Format.MODULE
|
|
95
116
|
break
|
|
96
117
|
case 'application/json':
|
|
97
|
-
format =
|
|
118
|
+
format = json_modules ? Format.JSON : null
|
|
98
119
|
break
|
|
99
120
|
case 'application/wasm':
|
|
100
|
-
format =
|
|
121
|
+
format = wasm_modules ? Format.WASM : null
|
|
101
122
|
break
|
|
102
123
|
default:
|
|
103
124
|
break
|
|
@@ -149,8 +170,8 @@ const getFormat = async (
|
|
|
149
170
|
format = extension_format_map.get(ext)!
|
|
150
171
|
|
|
151
172
|
switch (true) {
|
|
152
|
-
case format === Format.JSON && !
|
|
153
|
-
case format === Format.WASM && !
|
|
173
|
+
case format === Format.JSON && !json_modules:
|
|
174
|
+
case format === Format.WASM && !wasm_modules:
|
|
154
175
|
format = ignore_errors ? undefined : null
|
|
155
176
|
break
|
|
156
177
|
default:
|
|
@@ -200,7 +221,7 @@ const getFormat = async (
|
|
|
200
221
|
break
|
|
201
222
|
case 'http:':
|
|
202
223
|
case 'https:':
|
|
203
|
-
if (
|
|
224
|
+
if (network_imports) {
|
|
204
225
|
const { default: fetch } = await import('node-fetch')
|
|
205
226
|
const { headers } = await fetch(url.href, req)
|
|
206
227
|
format = mimeToFormat(headers.get('content-type'), true)
|
package/src/utils/get-source.ts
CHANGED
|
@@ -5,11 +5,15 @@
|
|
|
5
5
|
|
|
6
6
|
import { Format } from '#src/enums'
|
|
7
7
|
import type { GetSourceOptions } from '#src/interfaces'
|
|
8
|
+
import validateBoolean from '#src/internal/validate-boolean'
|
|
9
|
+
import validateObject from '#src/internal/validate-object'
|
|
10
|
+
import validateString from '#src/internal/validate-string'
|
|
8
11
|
import type { ModuleId } from '#src/types'
|
|
9
12
|
import {
|
|
10
13
|
ERR_UNSUPPORTED_ESM_URL_SCHEME,
|
|
11
14
|
type NodeError
|
|
12
15
|
} from '@flex-development/errnode'
|
|
16
|
+
import { isUndefined } from '@flex-development/tutils'
|
|
13
17
|
import fs from 'node:fs/promises'
|
|
14
18
|
import os from 'node:os'
|
|
15
19
|
import type { URL } from 'node:url'
|
|
@@ -20,6 +24,13 @@ import toURL from './to-url'
|
|
|
20
24
|
/**
|
|
21
25
|
* Retrieves source code for the given module `id`.
|
|
22
26
|
*
|
|
27
|
+
* ::: tip
|
|
28
|
+
* The given module `id` should be absolute (i.e. a [`file:` URL][1] or absolute
|
|
29
|
+
* specifier).
|
|
30
|
+
* :::
|
|
31
|
+
*
|
|
32
|
+
* [1]: https://nodejs.org/api/esm.html#file-urls
|
|
33
|
+
*
|
|
23
34
|
* @see {@linkcode GetSourceOptions}
|
|
24
35
|
* @see {@linkcode ModuleId}
|
|
25
36
|
* @see https://nodejs.org/docs/latest-v19.x/api/esm.html#loadurl-context-nextload
|
|
@@ -36,13 +47,17 @@ const getSource = async (
|
|
|
36
47
|
options: GetSourceOptions = {}
|
|
37
48
|
): Promise<Uint8Array | string | undefined> => {
|
|
38
49
|
const {
|
|
39
|
-
|
|
40
|
-
experimental_network_imports = false,
|
|
50
|
+
experimental_network_imports: network_imports = false,
|
|
41
51
|
format,
|
|
42
52
|
ignore_errors = false,
|
|
43
|
-
req
|
|
53
|
+
req = {}
|
|
44
54
|
} = options
|
|
45
55
|
|
|
56
|
+
validateBoolean(network_imports, 'options.experimental_network_imports')
|
|
57
|
+
!isUndefined(format) && validateString(format, 'options.format')
|
|
58
|
+
validateBoolean(ignore_errors, 'options.ignore_errors')
|
|
59
|
+
!isUndefined(req) && validateObject(req, 'options.req')
|
|
60
|
+
|
|
46
61
|
// exit early if format is Format.BUILTIN
|
|
47
62
|
if (format === Format.BUILTIN) return undefined
|
|
48
63
|
|
|
@@ -51,7 +66,7 @@ const getSource = async (
|
|
|
51
66
|
*
|
|
52
67
|
* @const {URL} url
|
|
53
68
|
*/
|
|
54
|
-
const url: URL = toURL(id
|
|
69
|
+
const url: URL = toURL(id)
|
|
55
70
|
|
|
56
71
|
/**
|
|
57
72
|
* [`ERR_UNSUPPORTED_ESM_URL_SCHEME`][1] check.
|
|
@@ -89,7 +104,7 @@ const getSource = async (
|
|
|
89
104
|
break
|
|
90
105
|
case 'http:':
|
|
91
106
|
case 'https:':
|
|
92
|
-
if (
|
|
107
|
+
if (network_imports) {
|
|
93
108
|
const { default: fetch } = await import('node-fetch')
|
|
94
109
|
source = await (await fetch(url.href, req)).text()
|
|
95
110
|
} else {
|
|
@@ -114,7 +129,7 @@ const getSource = async (
|
|
|
114
129
|
const schemes: string[] = ['data', 'file']
|
|
115
130
|
|
|
116
131
|
// update supported schemes if support for network based modules is enabled
|
|
117
|
-
if (
|
|
132
|
+
if (network_imports) schemes.push('http', 'https')
|
|
118
133
|
|
|
119
134
|
throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(
|
|
120
135
|
url,
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* @module mlly/utils/isAbsoluteSpecifier
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import validateString from '#src/internal/validate-string'
|
|
7
|
+
import type { NodeError } from '@flex-development/errnode'
|
|
6
8
|
import pathe from '@flex-development/pathe'
|
|
7
9
|
import { URL } from 'node:url'
|
|
8
10
|
|
|
@@ -18,8 +20,11 @@ import { URL } from 'node:url'
|
|
|
18
20
|
*
|
|
19
21
|
* @param {string} specifier - Specifier to evaluate
|
|
20
22
|
* @return {boolean} `true` if `specifier` is absolute specifier
|
|
23
|
+
* @throws {NodeError<TypeError>} If `specifier` is not a string
|
|
21
24
|
*/
|
|
22
25
|
const isAbsoluteSpecifier = (specifier: string): boolean => {
|
|
26
|
+
validateString(specifier, 'specifier')
|
|
27
|
+
|
|
23
28
|
/**
|
|
24
29
|
* Absolute specifier check.
|
|
25
30
|
*
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* @module mlly/utils/isBareSpecifier
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import validateString from '#src/internal/validate-string'
|
|
7
|
+
import type { NodeError } from '@flex-development/errnode'
|
|
6
8
|
import isAbsoluteSpecifier from './is-absolute-specifier'
|
|
7
9
|
import isRelativeSpecifier from './is-relative-specifier'
|
|
8
10
|
|
|
@@ -18,8 +20,11 @@ import isRelativeSpecifier from './is-relative-specifier'
|
|
|
18
20
|
*
|
|
19
21
|
* @param {string} specifier - Specifier to evaluate
|
|
20
22
|
* @return {boolean} `true` if `specifier` is bare specifier
|
|
23
|
+
* @throws {NodeError<TypeError>} If `specifier` is not a string
|
|
21
24
|
*/
|
|
22
25
|
const isBareSpecifier = (specifier: string): boolean => {
|
|
26
|
+
validateString(specifier, 'specifier')
|
|
27
|
+
|
|
23
28
|
return (
|
|
24
29
|
specifier.trim().length > 0 &&
|
|
25
30
|
!isAbsoluteSpecifier(specifier) &&
|
|
@@ -65,7 +65,7 @@ const isExportsSugar = (
|
|
|
65
65
|
validateExports(exports, pkg, parent)
|
|
66
66
|
|
|
67
67
|
// check for exports sugar
|
|
68
|
-
sugar = !Object.getOwnPropertyNames(exports)[0]?.startsWith('.')
|
|
68
|
+
sugar = !(Object.getOwnPropertyNames(exports)[0]?.startsWith('.') ?? true)
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
return sugar
|
|
@@ -23,6 +23,7 @@ import pathe from '@flex-development/pathe'
|
|
|
23
23
|
*/
|
|
24
24
|
const isRelativeSpecifier = (specifier: string): boolean => {
|
|
25
25
|
validateString(specifier, 'specifier')
|
|
26
|
+
|
|
26
27
|
return specifier.startsWith('.')
|
|
27
28
|
? specifier.length === 1 || specifier[1] === pathe.sep
|
|
28
29
|
? true
|
|
@@ -7,6 +7,7 @@ import type { ParseModuleIdOptions, ParsedModuleId } from '#src/interfaces'
|
|
|
7
7
|
import ENCODED_SEP_REGEX from '#src/internal/regex-encoded-sep'
|
|
8
8
|
import INTERNAL_SPECIFIER_REGEX from '#src/internal/regex-internal-specifier'
|
|
9
9
|
import PACKAGE_PATH_REGEX from '#src/internal/regex-package-path'
|
|
10
|
+
import validateBoolean from '#src/internal/validate-boolean'
|
|
10
11
|
import validateURLString from '#src/internal/validate-url-string'
|
|
11
12
|
import type { ModuleId } from '#src/types'
|
|
12
13
|
import {
|
|
@@ -30,9 +31,7 @@ import toNodeURL from './to-node-url'
|
|
|
30
31
|
* @param {ModuleId} id - Module id to parse
|
|
31
32
|
* @param {ParseModuleIdOptions?} [options={}] - Parsing options
|
|
32
33
|
* @return {ParsedModuleId} Object representing `id`
|
|
33
|
-
* @throws {NodeError<TypeError>}
|
|
34
|
-
* or a string, if `id` includes encoded path separators, or if `id` is invalid
|
|
35
|
-
* according to `options.internal` or `options.pkgname`
|
|
34
|
+
* @throws {NodeError<TypeError>}
|
|
36
35
|
*/
|
|
37
36
|
const parseModuleId = (
|
|
38
37
|
id: ModuleId,
|
|
@@ -46,6 +45,10 @@ const parseModuleId = (
|
|
|
46
45
|
// ensure id is an instance of URL or a string
|
|
47
46
|
validateURLString(id, 'id')
|
|
48
47
|
|
|
48
|
+
// ensure option schemas
|
|
49
|
+
validateBoolean(internal, 'options.internal')
|
|
50
|
+
validateBoolean(pkgname, 'options.pkgname')
|
|
51
|
+
|
|
49
52
|
// ensure id a string without leading and trailing spaces
|
|
50
53
|
id = id instanceof URL ? id.href : id.trim()
|
|
51
54
|
|
|
@@ -9,18 +9,28 @@ import type {
|
|
|
9
9
|
ParsedSubpath
|
|
10
10
|
} from '#src/interfaces'
|
|
11
11
|
import getSubpaths from '#src/internal/get-subpaths'
|
|
12
|
+
import isArrayIndex from '#src/internal/is-array-index'
|
|
13
|
+
import invalidSegmentRegex from '#src/internal/regex-invalid-segment'
|
|
14
|
+
import PACKAGE_NAME_REGEX from '#src/internal/regex-package-name'
|
|
15
|
+
import validateArraySet from '#src/internal/validate-array-set'
|
|
16
|
+
import validateBoolean from '#src/internal/validate-boolean'
|
|
12
17
|
import validateString from '#src/internal/validate-string'
|
|
13
18
|
import validateURLString from '#src/internal/validate-url-string'
|
|
14
19
|
import {
|
|
20
|
+
ERR_INVALID_PACKAGE_CONFIG,
|
|
21
|
+
ERR_INVALID_PACKAGE_TARGET,
|
|
15
22
|
ERR_PACKAGE_IMPORT_NOT_DEFINED,
|
|
16
23
|
ERR_PACKAGE_PATH_NOT_EXPORTED,
|
|
24
|
+
ErrorCode,
|
|
17
25
|
type NodeError
|
|
18
26
|
} from '@flex-development/errnode'
|
|
19
27
|
import pathe from '@flex-development/pathe'
|
|
20
28
|
import type { Exports, Imports } from '@flex-development/pkg-types'
|
|
21
|
-
import { CompareResult, type Nullable } from '@flex-development/tutils'
|
|
29
|
+
import { CompareResult, isNIL, type Nullable } from '@flex-development/tutils'
|
|
22
30
|
import { URL, fileURLToPath, pathToFileURL } from 'node:url'
|
|
23
31
|
import compareSubpaths from './compare-subpaths'
|
|
32
|
+
import CONDITIONS from './conditions'
|
|
33
|
+
import isExportsSugar from './is-exports-sugar'
|
|
24
34
|
import parseModuleId from './parse-module-id'
|
|
25
35
|
import PATTERN_CHARACTER from './pattern-character'
|
|
26
36
|
|
|
@@ -48,21 +58,30 @@ import PATTERN_CHARACTER from './pattern-character'
|
|
|
48
58
|
* @param {Exports | Imports | undefined} context - Package context
|
|
49
59
|
* @param {ParseSubpathOptions} options - Parsing options
|
|
50
60
|
* @return {ParsedSubpath} Object representing package subpath
|
|
51
|
-
* @throws {NodeError<Error | TypeError>}
|
|
52
|
-
* subpath defined in `specifier` is not defined in `context`
|
|
61
|
+
* @throws {NodeError<Error | TypeError>}
|
|
53
62
|
*/
|
|
54
63
|
const parseSubpath = (
|
|
55
64
|
specifier: string,
|
|
56
65
|
context: Exports | Imports | undefined,
|
|
57
66
|
options: ParseSubpathOptions
|
|
58
67
|
): ParsedSubpath => {
|
|
59
|
-
const {
|
|
68
|
+
const {
|
|
69
|
+
condition = 'default',
|
|
70
|
+
conditions = CONDITIONS,
|
|
71
|
+
dir,
|
|
72
|
+
internal = specifier.startsWith('#'),
|
|
73
|
+
parent
|
|
74
|
+
} = options
|
|
60
75
|
|
|
61
76
|
// ensure specifier is a string
|
|
62
77
|
validateString(specifier, 'specifier')
|
|
63
78
|
|
|
64
|
-
// ensure
|
|
79
|
+
// ensure option schemas
|
|
80
|
+
validateString(condition, 'options.condition')
|
|
81
|
+
validateArraySet(conditions, 'options.conditions')
|
|
65
82
|
validateURLString(dir, 'options.dir')
|
|
83
|
+
validateBoolean(internal, 'options.internal')
|
|
84
|
+
validateURLString(parent, 'options.parent')
|
|
66
85
|
|
|
67
86
|
/**
|
|
68
87
|
* Parsed module id.
|
|
@@ -156,9 +175,6 @@ const parseSubpath = (
|
|
|
156
175
|
if (key === null) {
|
|
157
176
|
let { parent } = options
|
|
158
177
|
|
|
159
|
-
// ensure parent is an instance of URL or a string
|
|
160
|
-
validateURLString(parent, 'options.parent')
|
|
161
|
-
|
|
162
178
|
// ensure parent is a path
|
|
163
179
|
parent = fileURLToPath(parent)
|
|
164
180
|
|
|
@@ -167,7 +183,136 @@ const parseSubpath = (
|
|
|
167
183
|
: new ERR_PACKAGE_PATH_NOT_EXPORTED(pkgdir, id.path, parent)
|
|
168
184
|
}
|
|
169
185
|
|
|
170
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Finds the package target string specified by {@linkcode key}.
|
|
188
|
+
*
|
|
189
|
+
* @param {Exports | undefined} data - Initial package target
|
|
190
|
+
* @return {Nullable<string>} Package target string or `null`
|
|
191
|
+
* @throws {NodeError<Error | TypeError>}
|
|
192
|
+
*/
|
|
193
|
+
const findPackageTarget = (data: Exports | undefined): Nullable<string> => {
|
|
194
|
+
/**
|
|
195
|
+
* Package target.
|
|
196
|
+
*
|
|
197
|
+
* @var {Nullable<string>} target
|
|
198
|
+
*/
|
|
199
|
+
let target: Nullable<string> = null
|
|
200
|
+
|
|
201
|
+
switch (true) {
|
|
202
|
+
case isNIL(data):
|
|
203
|
+
target = null
|
|
204
|
+
break
|
|
205
|
+
case Array.isArray(data):
|
|
206
|
+
/**
|
|
207
|
+
* Possible package target search error.
|
|
208
|
+
*
|
|
209
|
+
* @var {NodeError | undefined} error
|
|
210
|
+
*/
|
|
211
|
+
let error: NodeError | undefined
|
|
212
|
+
|
|
213
|
+
// try finding package target based on first match in search context
|
|
214
|
+
for (const item of data as string[]) {
|
|
215
|
+
try {
|
|
216
|
+
target = findPackageTarget(item)
|
|
217
|
+
} catch (e: unknown) {
|
|
218
|
+
error = e as NodeError
|
|
219
|
+
|
|
220
|
+
/* c8 ignore next */
|
|
221
|
+
if (error.code !== ErrorCode.ERR_INVALID_PACKAGE_TARGET) throw error
|
|
222
|
+
|
|
223
|
+
continue
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// stop search attempts if target was found
|
|
227
|
+
if (target) {
|
|
228
|
+
error = undefined
|
|
229
|
+
break
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// throw if error was encountered
|
|
234
|
+
if (error) throw error
|
|
235
|
+
|
|
236
|
+
break
|
|
237
|
+
case typeof data === 'object':
|
|
238
|
+
data = data as Record<string, Exports>
|
|
239
|
+
|
|
240
|
+
// try finding package target based on condition
|
|
241
|
+
for (const property of Object.getOwnPropertyNames(data)) {
|
|
242
|
+
if (isArrayIndex(property)) {
|
|
243
|
+
throw new ERR_INVALID_PACKAGE_CONFIG(
|
|
244
|
+
fileURLToPath(pkg),
|
|
245
|
+
fileURLToPath(parent),
|
|
246
|
+
'"exports" cannot contain numeric property keys'
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// check conditions
|
|
251
|
+
if (property === condition || new Set(conditions).has(property)) {
|
|
252
|
+
target = findPackageTarget(data[property])
|
|
253
|
+
if (target) break
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
break
|
|
258
|
+
case typeof data === 'string':
|
|
259
|
+
target = data as string
|
|
260
|
+
|
|
261
|
+
switch (true) {
|
|
262
|
+
case internal && PACKAGE_NAME_REGEX.test(target):
|
|
263
|
+
break
|
|
264
|
+
case target.startsWith('.' + pathe.sep):
|
|
265
|
+
// check target for invalid segments
|
|
266
|
+
if (invalidSegmentRegex().test(target.slice(2))) {
|
|
267
|
+
if (invalidSegmentRegex('deprecated').test(target.slice(2))) {
|
|
268
|
+
throw new ERR_INVALID_PACKAGE_TARGET(
|
|
269
|
+
fileURLToPath(dir),
|
|
270
|
+
key!,
|
|
271
|
+
target,
|
|
272
|
+
internal,
|
|
273
|
+
fileURLToPath(parent)
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
break
|
|
279
|
+
default:
|
|
280
|
+
throw new ERR_INVALID_PACKAGE_TARGET(
|
|
281
|
+
fileURLToPath(dir),
|
|
282
|
+
key!,
|
|
283
|
+
target,
|
|
284
|
+
internal,
|
|
285
|
+
fileURLToPath(parent)
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
break
|
|
290
|
+
default:
|
|
291
|
+
throw new ERR_INVALID_PACKAGE_TARGET(
|
|
292
|
+
fileURLToPath(dir),
|
|
293
|
+
key!,
|
|
294
|
+
data,
|
|
295
|
+
internal,
|
|
296
|
+
fileURLToPath(parent)
|
|
297
|
+
)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return target
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// convert exports to object if using exports main sugar
|
|
304
|
+
if (!internal && isExportsSugar(context, pkg, parent)) {
|
|
305
|
+
context = { '.': context } as Record<string, Exports>
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
base,
|
|
310
|
+
internal,
|
|
311
|
+
key,
|
|
312
|
+
raw: id.path,
|
|
313
|
+
specifier: id.raw,
|
|
314
|
+
target: findPackageTarget((context as Record<string, Exports>)[key])
|
|
315
|
+
}
|
|
171
316
|
}
|
|
172
317
|
|
|
173
318
|
export default parseSubpath
|
|
@@ -13,7 +13,12 @@ import {
|
|
|
13
13
|
} from '@flex-development/errnode'
|
|
14
14
|
import pathe from '@flex-development/pathe'
|
|
15
15
|
import type { PackageJson } from '@flex-development/pkg-types'
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
isEmptyString,
|
|
18
|
+
isNIL,
|
|
19
|
+
isUndefined,
|
|
20
|
+
type Nullable
|
|
21
|
+
} from '@flex-development/tutils'
|
|
17
22
|
import fs from 'node:fs'
|
|
18
23
|
import { fileURLToPath } from 'node:url'
|
|
19
24
|
import toURL from './to-url'
|
|
@@ -44,10 +49,10 @@ const readPackageJson = (
|
|
|
44
49
|
validateURLString(dir, 'dir')
|
|
45
50
|
|
|
46
51
|
// ensure specifier is a string
|
|
47
|
-
|
|
52
|
+
!isUndefined(specifier) && validateString(specifier, 'specifier')
|
|
48
53
|
|
|
49
54
|
// ensure parent is an instance of URL or a string
|
|
50
|
-
|
|
55
|
+
!isUndefined(parent) && validateURLString(parent, 'parent')
|
|
51
56
|
|
|
52
57
|
// ensure dir is a path
|
|
53
58
|
dir = fileURLToPath(toURL(dir))
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
import type { ParsedModuleId, ResolveAliasOptions } from '#src/interfaces'
|
|
7
7
|
import regexp from '#src/internal/escape-reg-exp'
|
|
8
|
+
import validateArraySet from '#src/internal/validate-array-set'
|
|
8
9
|
import validateBoolean from '#src/internal/validate-boolean'
|
|
9
10
|
import validateObject from '#src/internal/validate-object'
|
|
10
|
-
import validateSet from '#src/internal/validate-set'
|
|
11
11
|
import validateString from '#src/internal/validate-string'
|
|
12
12
|
import validateURLString from '#src/internal/validate-url-string'
|
|
13
13
|
import type { NodeError } from '@flex-development/errnode'
|
|
@@ -60,8 +60,9 @@ const resolveAlias = async (
|
|
|
60
60
|
validateBoolean(absolute, 'options.absolute')
|
|
61
61
|
validateObject(aliases, 'options.aliases')
|
|
62
62
|
validateString(condition, 'options.condition')
|
|
63
|
+
validateArraySet(conditions, 'options.conditions')
|
|
63
64
|
validateURLString(cwd, 'options.cwd')
|
|
64
|
-
|
|
65
|
+
validateArraySet(extensions, 'options.extensions')
|
|
65
66
|
validateURLString(parent, 'options.parent')
|
|
66
67
|
validateBoolean(preserveSymlinks, 'options.preserveSymlinks')
|
|
67
68
|
|