bare-url 0.0.0 → 0.1.1

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/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # bare-url
2
2
 
3
- URL parser for Javascript
3
+ URL parser for JavaScript.
4
4
 
5
5
  ```
6
- npm install bare-url
6
+ npm i bare-url
7
7
  ```
8
8
 
9
9
  ## Usage
10
10
 
11
- ``` js
11
+ ```js
12
12
  const url = require('bare-url')
13
13
 
14
14
  const p = url.fileURLToPath('file:///foo') // --> /foo
package/index.js CHANGED
@@ -1,8 +1,222 @@
1
1
  const path = require('path')
2
+ const constants = require('./lib/constants')
3
+ const errors = require('./lib/errors')
4
+ const parse = require('./lib/parse')
5
+ const serialize = require('./lib/serialize')
2
6
 
3
- exports.fileURLToPath = fileURLToPath
7
+ const URL = exports.URL = class URL {
8
+ constructor (href, base) {
9
+ if (typeof base === 'string') {
10
+ try {
11
+ base = new URL(base)
12
+ } catch (err) {
13
+ err.message = 'Invalid base URL'
14
+ throw err
15
+ }
16
+ }
4
17
 
5
- function fileURLToPath (u) {
6
- if (u.startsWith('file://')) u = u.slice(7)
7
- return path.normalize(u)
18
+ this._url = parse(href, base ? base._url : null)
19
+ }
20
+
21
+ // https://url.spec.whatwg.org/#dom-url-href
22
+
23
+ get href () {
24
+ return serialize(this._url)
25
+ }
26
+
27
+ set href (value) {
28
+ this._url = parse(value)
29
+ }
30
+
31
+ // https://url.spec.whatwg.org/#dom-url-protocol
32
+
33
+ get protocol () {
34
+ return `${this._url.scheme}:`
35
+ }
36
+
37
+ set protocol (value) {
38
+ parse(`${value}:`, null, this._url, constants.STATE_SCHEME_START)
39
+ }
40
+
41
+ // https://url.spec.whatwg.org/#dom-url-username
42
+
43
+ get username () {
44
+ return this._url.username
45
+ }
46
+
47
+ set username (value) {
48
+ if (this._url.host === null || this._url.host === '' || this._url.scheme === 'file') {
49
+ return
50
+ }
51
+
52
+ this._url.username = encodeURIComponent(value)
53
+ }
54
+
55
+ // https://url.spec.whatwg.org/#dom-url-password
56
+
57
+ get password () {
58
+ return this._url.password
59
+ }
60
+
61
+ set password (value) {
62
+ if (this._url.host === null || this._url.host === '' || this._url.scheme === 'file') {
63
+ return
64
+ }
65
+
66
+ this._url.password = encodeURIComponent(value)
67
+ }
68
+
69
+ // https://url.spec.whatwg.org/#dom-url-host
70
+
71
+ get host () {
72
+ if (this._url.host === null) return ''
73
+ if (this._url.port === null) return this._url.host
74
+
75
+ return `${this._url.host}:${this._url.port}`
76
+ }
77
+
78
+ set host (value) {
79
+ if (typeof this._url.path === 'string') return
80
+
81
+ parse(value, null, this._url, constants.STATE_HOST)
82
+ }
83
+
84
+ // https://url.spec.whatwg.org/#dom-url-hostname
85
+
86
+ get hostname () {
87
+ if (this._url.host === null) return ''
88
+
89
+ return this._url.host
90
+ }
91
+
92
+ set hostname (value) {
93
+ if (typeof this._url.path === 'string') return
94
+
95
+ parse(value, null, this._url, constants.STATE_HOSTNAME)
96
+ }
97
+
98
+ // https://url.spec.whatwg.org/#dom-url-port
99
+
100
+ get port () {
101
+ if (this._url.port === null) return ''
102
+
103
+ return `${this._url.port}`
104
+ }
105
+
106
+ set port (value) {
107
+ if (this._url.host === null || this._url.host === '' || this._url.scheme === 'file') {
108
+ return
109
+ }
110
+
111
+ if (value === '') {
112
+ this._url.port = null
113
+ } else {
114
+ parse(value, null, this._url, constants.STATE_PORT)
115
+ }
116
+ }
117
+
118
+ // https://url.spec.whatwg.org/#dom-url-pathname
119
+
120
+ get pathname () {
121
+ if (typeof this._url.path === 'string') return this._url.path
122
+
123
+ let output = ''
124
+
125
+ for (const segment of this._url.path) output += `/${segment}`
126
+
127
+ return output
128
+ }
129
+
130
+ set pathname (value) {
131
+ if (typeof this._url.path === 'string') return
132
+
133
+ this._url.path = []
134
+
135
+ parse(value, null, this._url, constants.STATE_PATH_START)
136
+ }
137
+
138
+ // https://url.spec.whatwg.org/#dom-url-search
139
+
140
+ get search () {
141
+ if (this._url.query === null || this._url.query === '') return ''
142
+
143
+ return `?${this._url.query}`
144
+ }
145
+
146
+ set search (value) {
147
+ if (value === '') {
148
+ this._url.query = null
149
+
150
+ return
151
+ }
152
+
153
+ if (value.charCodeAt(0) === 0x3f) {
154
+ value = value.substring(1)
155
+ }
156
+
157
+ this._url.query = ''
158
+
159
+ parse(value, null, this._url, constants.STATE_QUERY)
160
+ }
161
+
162
+ // https://url.spec.whatwg.org/#dom-url-hash
163
+
164
+ get hash () {
165
+ if (this._url.fragment === null || this._url.fragment === '') return ''
166
+
167
+ return `#${this._url.fragment}`
168
+ }
169
+
170
+ set hash (value) {
171
+ if (value === '') {
172
+ this._url.fragment = null
173
+
174
+ return
175
+ }
176
+
177
+ if (value.charCodeAt(0) === 0x23) {
178
+ value = value.substring(1)
179
+ }
180
+
181
+ this._url.fragment = ''
182
+
183
+ parse(value, null, this._url, constants.STATE_FRAGMENT)
184
+ }
185
+
186
+ [Symbol.for('bare.inspect')] () {
187
+ return {
188
+ __proto__: { constructor: URL },
189
+
190
+ href: this.href,
191
+ protocol: this.protocol,
192
+ username: this.username,
193
+ password: this.password,
194
+ host: this.host,
195
+ hostname: this.hostname,
196
+ port: this.port,
197
+ pathname: this.pathname,
198
+ search: this.search,
199
+ hash: this.hash
200
+ }
201
+ }
202
+ }
203
+
204
+ exports.fileURLToPath = function fileURLToPath (url) {
205
+ if (typeof url === 'string') {
206
+ url = new URL(url)
207
+ }
208
+
209
+ if (url.protocol !== 'file:') {
210
+ throw errors.INVALID_URL_SCHEME('The URL must use the file: protocol')
211
+ }
212
+
213
+ const pathname = path.normalize(decodeURIComponent(url.pathname))
214
+
215
+ if (process.platform === 'win32') {
216
+ if (url.hostname) return `\\\\${url.hostname}${pathname}`
217
+
218
+ return pathname.slice(1)
219
+ }
220
+
221
+ return pathname
8
222
  }
@@ -0,0 +1,25 @@
1
+ module.exports = {
2
+ // Parser states
3
+ // https://url.spec.whatwg.org/#url-parsing
4
+ STATE_SCHEME_START: 1,
5
+ STATE_SCHEME: 2,
6
+ STATE_NO_SCHEME: 3,
7
+ STATE_SPECIAL_RELATIVE_OR_AUTHORITY: 4,
8
+ STATE_PATH_OR_AUTHORITY: 5,
9
+ STATE_RELATIVE: 6,
10
+ STATE_RELATIVE_SLASH: 7,
11
+ STATE_SPECIAL_AUTHORITY_SLASHES: 8,
12
+ STATE_SPECIAL_AUTHORITY_IGNORE_SLASHES: 9,
13
+ STATE_AUTHORITY: 10,
14
+ STATE_HOST: 11,
15
+ STATE_HOSTNAME: 12,
16
+ STATE_PORT: 13,
17
+ STATE_FILE: 14,
18
+ STATE_FILE_SLASH: 15,
19
+ STATE_FILE_HOST: 16,
20
+ STATE_PATH_START: 17,
21
+ STATE_PATH: 18,
22
+ STATE_OPAQUE_PATH: 19,
23
+ STATE_QUERY: 20,
24
+ STATE_FRAGMENT: 21
25
+ }
package/lib/errors.js ADDED
@@ -0,0 +1,47 @@
1
+ module.exports = class URLError extends Error {
2
+ constructor (msg, code, fn = URLError) {
3
+ super(`${code}: ${msg}`)
4
+ this.code = code
5
+
6
+ if (Error.captureStackTrace) {
7
+ Error.captureStackTrace(this, fn)
8
+ }
9
+ }
10
+
11
+ get name () {
12
+ return 'URLError'
13
+ }
14
+
15
+ static INVALID_URL (msg = 'Invalid URL') {
16
+ return new URLError(msg, 'INVALID_URL', URLError.INVALID_URL)
17
+ }
18
+
19
+ static INVALID_URL_SCHEME (msg = 'Invalid URL') {
20
+ return new URLError(msg, 'INVALID_URL_SCHEME', URLError.INVALID_URL_SCHEME)
21
+ }
22
+
23
+ // https://url.spec.whatwg.org/#missing-scheme-non-relative-url
24
+ static MISSING_SCHEME_NON_RELATIVE_URL (msg = 'Invalid URL') {
25
+ return new URLError(msg, 'MISSING_SCHEME_NON_RELATIVE_URL', URLError.MISSING_SCHEME_NON_RELATIVE_URL)
26
+ }
27
+
28
+ // https://url.spec.whatwg.org/#invalid-credentials
29
+ static INVALID_CREDENTIALS (msg = 'Invalid URL') {
30
+ return new URLError(msg, 'INVALID_CREDENTIALS', URLError.INVALID_CREDENTIALS)
31
+ }
32
+
33
+ // https://url.spec.whatwg.org/#host-missing
34
+ static HOST_MISSING (msg = 'Invalid URL') {
35
+ return new URLError(msg, 'HOST_MISSING', URLError.HOST_MISSING)
36
+ }
37
+
38
+ // https://url.spec.whatwg.org/#port-out-of-range
39
+ static PORT_OUT_OF_RANGE (msg = 'Invalid URL') {
40
+ return new URLError(msg, 'PORT_OUT_OF_RANGE', URLError.PORT_OUT_OF_RANGE)
41
+ }
42
+
43
+ // https://url.spec.whatwg.org/#port-invalid
44
+ static PORT_INVALID (msg = 'Invalid URL') {
45
+ return new URLError(msg, 'PORT_INVALID', URLError.PORT_INVALID)
46
+ }
47
+ }
package/lib/infra.js ADDED
@@ -0,0 +1,39 @@
1
+ // https://infra.spec.whatwg.org/#ascii-digit
2
+ exports.isASCIIDigit = function isASCIIDigit (c) {
3
+ return c >= 0x30 && c <= 0x39
4
+ }
5
+
6
+ // https://infra.spec.whatwg.org/#ascii-upper-hex-digit
7
+ exports.isASCIIUpperHexDigit = function isASCIIUpperHexDigit (c) {
8
+ return exports.isASCIIDigit(c) || (c >= 0x41 && c <= 0x46)
9
+ }
10
+
11
+ // https://infra.spec.whatwg.org/#ascii-lower-hex-digit
12
+ exports.isASCIILowerHexDigit = function isASCIILowerHexDigit (c) {
13
+ return exports.isASCIIDigit(c) || (c >= 0x61 && c <= 0x66)
14
+ }
15
+
16
+ // https://infra.spec.whatwg.org/#ascii-hex-digit
17
+ exports.isASCIIHexDigit = function isASCIIHexDigit (c) {
18
+ return exports.isASCIIDigit(c) || (c >= 0x41 && c <= 0x46) || (c >= 0x61 && c <= 0x66)
19
+ }
20
+
21
+ // https://infra.spec.whatwg.org/#ascii-upper-alpha
22
+ exports.isASCIIUpperAlpha = function isASCIIUpperAlpha (c) {
23
+ return c >= 0x41 && c <= 0x5a
24
+ }
25
+
26
+ // https://infra.spec.whatwg.org/#ascii-lower-alpha
27
+ exports.isASCIILowerAlpha = function isASCIILowerAlpha (c) {
28
+ return c >= 0x61 && c <= 0x7a
29
+ }
30
+
31
+ // https://infra.spec.whatwg.org/#ascii-alpha
32
+ exports.isASCIIAlpha = function isASCIIAlpha (c) {
33
+ return exports.isASCIIUpperAlpha(c) || exports.isASCIILowerAlpha(c)
34
+ }
35
+
36
+ // https://infra.spec.whatwg.org/#ascii-alphanumeric
37
+ exports.isASCIIAlphanumeric = function isASCIIAlphanumeric (c) {
38
+ return exports.isASCIIDigit(c) || exports.isASCIIAlpha(c)
39
+ }
package/lib/parse.js ADDED
@@ -0,0 +1,602 @@
1
+ const constants = require('./constants')
2
+ const errors = require('./errors')
3
+ const infra = require('./infra')
4
+
5
+ // https://url.spec.whatwg.org/#url-parsing
6
+ module.exports = function parse (
7
+ input,
8
+ base = null,
9
+ // https://url.spec.whatwg.org/#url-representation
10
+ url = {
11
+ scheme: '',
12
+ username: '',
13
+ password: '',
14
+ host: null,
15
+ port: null,
16
+ path: [],
17
+ query: null,
18
+ fragment: null
19
+ },
20
+ stateOverride = 0
21
+ ) {
22
+ let state = stateOverride || constants.STATE_SCHEME_START
23
+ let buffer = ''
24
+ let atSignSeen = false
25
+ let insideBrackets = false
26
+ let passwordTokenSeen = false
27
+
28
+ for (let pointer = 0; pointer <= input.length; pointer++) {
29
+ const c = pointer < input.length ? input.charCodeAt(pointer) : -1
30
+
31
+ switch (state) {
32
+ // https://url.spec.whatwg.org/#scheme-start-state
33
+ case constants.STATE_SCHEME_START:
34
+ if (infra.isASCIIAlpha(c)) {
35
+ buffer += String.fromCharCode(c).toLowerCase()
36
+ state = constants.STATE_SCHEME
37
+ } else if (!stateOverride) {
38
+ state = constants.STATE_NO_SCHEME
39
+ pointer--
40
+ } else {
41
+ throw errors.INVALID_URL()
42
+ }
43
+ break
44
+
45
+ // https://url.spec.whatwg.org/#scheme-state
46
+ case constants.STATE_SCHEME:
47
+ if (infra.isASCIIAlphanumeric(c) || c === 0x2b || c === 0x2d || c === 0x2e) {
48
+ buffer += String.fromCharCode(c).toLowerCase()
49
+ } else if (c === 0x3a) {
50
+ if (stateOverride) {
51
+ if (isSpecialScheme(url.scheme) && !isSpecialScheme(buffer)) {
52
+ return
53
+ }
54
+
55
+ if (!isSpecialScheme(url.scheme) && isSpecialScheme(buffer)) {
56
+ return
57
+ }
58
+
59
+ if ((includesCredentials(url) || url.port !== null) && buffer === 'file') {
60
+ return
61
+ }
62
+
63
+ if (url.scheme === 'file' && url.host === '') {
64
+ return
65
+ }
66
+ }
67
+
68
+ url.scheme = buffer
69
+
70
+ if (stateOverride) {
71
+ if (url.port === defaultPort(url.scheme)) {
72
+ url.port = null
73
+ }
74
+
75
+ return
76
+ }
77
+
78
+ buffer = ''
79
+
80
+ if (url.scheme === 'file') {
81
+ state = constants.STATE_FILE
82
+ } else if (isSpecial(url)) {
83
+ if (base && base.scheme === url.scheme) {
84
+ state = constants.STATE_SPECIAL_RELATIVE_OR_AUTHORITY
85
+ } else {
86
+ state = constants.STATE_SPECIAL_AUTHORITY_SLASHES
87
+ }
88
+ } else if (input.charCodeAt(pointer + 1) === 0x2f) {
89
+ state = constants.STATE_PATH_OR_AUTHORITY
90
+ pointer++
91
+ } else {
92
+ url.path = ''
93
+ state = constants.STATE_OPAQUE_PATH
94
+ }
95
+ } else if (!stateOverride) {
96
+ buffer = ''
97
+ state = constants.STATE_NO_SCHEME
98
+ pointer = 0
99
+ } else {
100
+ throw errors.INVALID_URL()
101
+ }
102
+ break
103
+
104
+ // https://url.spec.whatwg.org/#no-scheme-state
105
+ case constants.STATE_NO_SCHEME:
106
+ if (base === null || (hasOpaquePath(base) && c !== 0x23)) {
107
+ throw errors.MISSING_SCHEME_NON_RELATIVE_URL()
108
+ }
109
+
110
+ if (hasOpaquePath(base) && c === 0x23) {
111
+ url.scheme = base.scheme
112
+ url.path = base.path
113
+ url.query = base.query
114
+ url.fragment = ''
115
+ state = constants.STATE_FRAGMENT
116
+ } else if (base.sceheme !== 'file') {
117
+ state = constants.STATE_RELATIVE
118
+ pointer--
119
+ } else {
120
+ state = constants.STATE_FILE
121
+ pointer++
122
+ }
123
+ break
124
+
125
+ // https://url.spec.whatwg.org/#special-relative-or-authority-state
126
+ case constants.STATE_SPECIAL_RELATIVE_OR_AUTHORITY:
127
+ if (c === 0x2f && input.charCodeAt(pointer + 1) === 0x2f) {
128
+ state = constants.STATE_SPECIAL_AUTHORITY_IGNORE_SLASHES
129
+ pointer++
130
+ } else {
131
+ state = constants.STATE_RELATIVE
132
+ pointer--
133
+ }
134
+ break
135
+
136
+ // https://url.spec.whatwg.org/#path-or-authority-state
137
+ case constants.STATE_PATH_OR_AUTHORITY:
138
+ if (c === 0x2f) {
139
+ state = constants.STATE_AUTHORITY
140
+ } else {
141
+ state = constants.STATE_PATH
142
+ pointer--
143
+ }
144
+ break
145
+
146
+ // https://url.spec.whatwg.org/#relative-state
147
+ case constants.STATE_RELATIVE:
148
+ url.scheme = base.scheme
149
+
150
+ if (c === 0x2f) {
151
+ state = constants.STATE_RELATIVE_SLASH
152
+ } else if (isSpecial(url) && c === 0x5c) {
153
+ state = constants.STATE_RELATIVE_SLASH
154
+ } else {
155
+ url.username = base.username
156
+ url.password = base.password
157
+ url.host = base.host
158
+ url.port = base.port
159
+ url.path = [...base.path]
160
+ url.query = base.query
161
+
162
+ if (c === 0x3f) {
163
+ url.query = ''
164
+ state = constants.STATE_QUERY
165
+ } else if (c === 0x23) {
166
+ url.fragment = ''
167
+ state = constants.STATE_FRAGMENT
168
+ } else if (c !== -1) {
169
+ url.query = null
170
+ shortenPath(url)
171
+ state = constants.STATE_PATH
172
+ pointer--
173
+ }
174
+ }
175
+ break
176
+
177
+ // https://url.spec.whatwg.org/#relative-slash-state
178
+ case constants.STATE_RELATIVE_SLASH:
179
+ if (isSpecial(url) && (c === 0x2f || c === 0x5c)) {
180
+ state = constants.STATE_SPECIAL_AUTHORITY_IGNORE_SLASHES
181
+ } else if (c === 0x2f) {
182
+ state = constants.STATE_AUTHORITY
183
+ } else {
184
+ url.username = base.username
185
+ url.password = base.password
186
+ url.host = base.url
187
+ url.port = base.port
188
+ state = constants.STATE_PATH
189
+ pointer--
190
+ }
191
+ break
192
+
193
+ // https://url.spec.whatwg.org/#special-authority-slashes-state
194
+ case constants.STATE_SPECIAL_AUTHORITY_SLASHES:
195
+ if (c === 0x2f && input.charCodeAt(pointer + 1) === 0x2f) {
196
+ state = constants.STATE_SPECIAL_AUTHORITY_IGNORE_SLASHES
197
+ pointer++
198
+ } else {
199
+ state = constants.STATE_SPECIAL_AUTHORITY_IGNORE_SLASHES
200
+ pointer--
201
+ }
202
+ break
203
+
204
+ // https://url.spec.whatwg.org/#special-authority-ignore-slashes-state
205
+ case constants.STATE_SPECIAL_AUTHORITY_IGNORE_SLASHES:
206
+ if (c !== 0x2f || c !== 0x5c) {
207
+ state = constants.STATE_AUTHORITY
208
+ pointer--
209
+ }
210
+ break
211
+
212
+ // https://url.spec.whatwg.org/#authority-state
213
+ case constants.STATE_AUTHORITY:
214
+ if (c === 0x40) {
215
+ if (atSignSeen) buffer = '%40' + buffer
216
+
217
+ atSignSeen = true
218
+
219
+ for (let i = 0, n = buffer.length; i < n; i++) {
220
+ const c = buffer.charCodeAt(i)
221
+
222
+ if (c === 0x3a && !passwordTokenSeen) {
223
+ passwordTokenSeen = true
224
+ continue
225
+ }
226
+
227
+ if (passwordTokenSeen) {
228
+ url.password += encodeURIComponent(buffer[i])
229
+ } else {
230
+ url.username += encodeURIComponent(buffer[i])
231
+ }
232
+ }
233
+
234
+ buffer = ''
235
+ } else if (
236
+ (c === -1 || c === 0x2f || c === 0x3f || c === 0x23) ||
237
+ (isSpecial(url) && c === 0x5c)
238
+ ) {
239
+ if (atSignSeen && buffer === '') {
240
+ throw errors.INVALID_CREDENTIALS()
241
+ }
242
+
243
+ pointer -= buffer.length + 1
244
+ buffer = ''
245
+ state = constants.STATE_HOST
246
+ } else {
247
+ buffer += String.fromCharCode(c)
248
+ }
249
+ break
250
+
251
+ // https://url.spec.whatwg.org/#host-state
252
+ // https://url.spec.whatwg.org/#hostname-state
253
+ case constants.STATE_HOST:
254
+ case constants.STATE_HOSTNAME:
255
+ if (stateOverride && url.scheme === 'file') {
256
+ pointer--
257
+ state = constants.STATE_FILE_HOST
258
+ } else if (c === 0x3a && !insideBrackets) {
259
+ if (buffer === '') {
260
+ throw errors.HOST_MISSING()
261
+ }
262
+
263
+ if (stateOverride === constants.STATE_HOSTNAME) return
264
+
265
+ url.host = parseHost(buffer, !isSpecial(url))
266
+
267
+ buffer = ''
268
+ state = constants.STATE_PORT
269
+ } else if (
270
+ (c === -1 || c === 0x2f || c === 0x3f || c === 0x23) ||
271
+ (isSpecial(url) && c === 0x5c)
272
+ ) {
273
+ pointer--
274
+
275
+ if (isSpecial(url) && buffer === '') {
276
+ throw errors.HOST_MISSING()
277
+ }
278
+
279
+ if (stateOverride && buffer === '' && (includesCredentials(url) || url.port !== null)) {
280
+ return
281
+ }
282
+
283
+ url.host = parseHost(buffer, !isSpecial(url))
284
+
285
+ buffer = ''
286
+ state = constants.STATE_PATH_START
287
+
288
+ if (stateOverride) return
289
+ } else {
290
+ if (c === 0x5b) insideBrackets = true
291
+ else if (c === 0x5d) insideBrackets = false
292
+
293
+ buffer += String.fromCharCode(c)
294
+ }
295
+ break
296
+
297
+ // https://url.spec.whatwg.org/#port-state
298
+ case constants.STATE_PORT:
299
+ if (infra.isASCIIDigit(c)) {
300
+ buffer += String.fromCharCode(c)
301
+ } else if (
302
+ (c === -1 || c === 0x2f || c === 0x3f || c === 0x23) ||
303
+ (isSpecial(url) && c === 0x5c) ||
304
+ stateOverride
305
+ ) {
306
+ if (buffer) {
307
+ const port = parseInt(buffer, 10)
308
+
309
+ if (port > 2 ** 16 - 1) {
310
+ throw errors.PORT_OUT_OF_RANGE()
311
+ }
312
+
313
+ url.port = port === defaultPort(url.scheme) ? null : port
314
+
315
+ buffer = ''
316
+ }
317
+
318
+ if (stateOverride) return
319
+
320
+ state = constants.STATE_PATH_START
321
+ pointer--
322
+ } else {
323
+ throw errors.PORT_INVALID()
324
+ }
325
+ break
326
+
327
+ // https://url.spec.whatwg.org/#file-state
328
+ case constants.STATE_FILE:
329
+ url.scheme = 'file'
330
+ url.host = ''
331
+
332
+ if (c === 0x2f || c === 0x5c) {
333
+ state = constants.STATE_FILE_SLASH
334
+ } else if (base && base.scheme === 'file') {
335
+ url.host = base.host
336
+ url.path = [...base.path]
337
+ url.query = base.query
338
+
339
+ if (c === 0x3f) {
340
+ url.query = ''
341
+ state = constants.STATE_QUERY
342
+ } else if (c === 0x23) {
343
+ url.fragment = ''
344
+ state = constants.STATE_FRAGMENT
345
+ } else if (c !== -1) {
346
+ url.query = null
347
+
348
+ if (!startsWithWindowsDriveLetter(input.substring(pointer))) {
349
+ shortenPath(url)
350
+ } else {
351
+ url.path = []
352
+ }
353
+
354
+ state = constants.STATE_PATH
355
+ pointer--
356
+ }
357
+ } else {
358
+ state = constants.STATE_PATH
359
+ pointer--
360
+ }
361
+ break
362
+
363
+ // https://url.spec.whatwg.org/#file-slash-state
364
+ case constants.STATE_FILE_SLASH:
365
+ if (c === 0x2f || c === 0x5c) {
366
+ state = constants.STATE_FILE_HOST
367
+ } else {
368
+ if (base && base.scheme === 'file') {
369
+ url.host = base.host
370
+
371
+ if (!startsWithWindowsDriveLetter(input.substring(pointer)) && isNormalizedWindowsDriveLetter(base.path[0])) {
372
+ url.path.push(base.path[0])
373
+ }
374
+ }
375
+
376
+ state = constants.STATE_PATH
377
+ pointer--
378
+ }
379
+ break
380
+
381
+ // https://url.spec.whatwg.org/#file-host-state
382
+ case constants.STATE_FILE_HOST:
383
+ if (c === -1 || c === 0x2f || c === 0x5c || c === 0x3f || c === 0x23) {
384
+ pointer--
385
+
386
+ if (!stateOverride && isWindowsDriveLetter(buffer)) {
387
+ state = constants.STATE_PATH
388
+ } else if (buffer === '') {
389
+ url.host = ''
390
+
391
+ if (stateOverride) return
392
+
393
+ state = constants.STATE_PATH_START
394
+ } else {
395
+ let host = parseHost(buffer, !isSpecial(url))
396
+ if (host === 'localhost') host = ''
397
+
398
+ url.host = host
399
+
400
+ if (stateOverride) return
401
+
402
+ buffer = ''
403
+ state = constants.STATE_PATH_START
404
+ }
405
+ } else {
406
+ buffer += String.fromCharCode(c)
407
+ }
408
+ break
409
+
410
+ // https://url.spec.whatwg.org/#path-start-state
411
+ case constants.STATE_PATH_START:
412
+ if (isSpecial(url)) {
413
+ state = constants.STATE_PATH
414
+
415
+ if (c !== 0x2f || c !== 0x5c) pointer--
416
+ } else if (!stateOverride && c === 0x3f) {
417
+ url.query = ''
418
+ state = constants.STATE_QUERY
419
+ } else if (!stateOverride && c === 0x23) {
420
+ url.fragment = ''
421
+ state = constants.STATE_FRAGMENT
422
+ } else if (c !== -1) {
423
+ state = constants.STATE_PATH
424
+
425
+ if (c !== 0x2f) pointer--
426
+ } else if (stateOverride && url.host === null) {
427
+ url.path.push('')
428
+ }
429
+ break
430
+
431
+ // https://url.spec.whatwg.org/#path-state
432
+ case constants.STATE_PATH:
433
+ if (
434
+ (c === -1 || c === 0x2f) ||
435
+ (isSpecial(url) && c === 0x5c) ||
436
+ (!stateOverride && (c === 0x3f || c === 0x23))
437
+ ) {
438
+ if (isDoubleDotPathSegment(buffer)) {
439
+ shortenPath(url)
440
+
441
+ if (c !== 0x2f || !(isSpecial(url) && c === 0x5c)) {
442
+ url.path.push('')
443
+ }
444
+ } else if (isSingleDotPathSegment(buffer)) {
445
+ if (c !== 0x2f || !(isSpecial(url) && c === 0x5c)) {
446
+ url.path.push('')
447
+ }
448
+ } else {
449
+ if (url.scheme === 'file' && url.path.length === 0 && isWindowsDriveLetter(buffer)) {
450
+ buffer[1] = ':'
451
+ }
452
+
453
+ url.path.push(buffer)
454
+ }
455
+
456
+ buffer = ''
457
+
458
+ if (c === 0x3f) {
459
+ url.query = ''
460
+ state = constants.STATE_QUERY
461
+ } else if (c === 0x23) {
462
+ url.fragment = ''
463
+ state = constants.STATE_FRAGMENT
464
+ }
465
+ } else {
466
+ buffer += encodeURIComponent(String.fromCharCode(c))
467
+ }
468
+ break
469
+
470
+ // https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
471
+ case constants.STATE_OPAQUE_PATH:
472
+ if (c === 0x3f) {
473
+ url.query = ''
474
+ state = constants.STATE_QUERY
475
+ } else if (c === 0x23) {
476
+ url.fragment = ''
477
+ state = constants.STATE_FRAGMENT
478
+ } else {
479
+ if (c !== -1) {
480
+ url.path += encodeURIComponent(String.fromCharCode(c))
481
+ }
482
+ }
483
+ break
484
+
485
+ // https://url.spec.whatwg.org/#query-state
486
+ case constants.STATE_QUERY:
487
+ if ((!stateOverride && c === 0x23) || c === -1) {
488
+ url.query += encodeURI(buffer)
489
+ buffer = ''
490
+
491
+ if (c === 0x23) {
492
+ url.fragment = ''
493
+ state = constants.STATE_FRAGMENT
494
+ }
495
+ } else if (c !== -1) {
496
+ buffer += String.fromCharCode(c)
497
+ }
498
+ break
499
+
500
+ // https://url.spec.whatwg.org/#fragment-state
501
+ case constants.STATE_FRAGMENT:
502
+ if (c !== -1) {
503
+ url.fragment += encodeURI(String.fromCharCode(c))
504
+ }
505
+ }
506
+ }
507
+
508
+ return url
509
+ }
510
+
511
+ // https://url.spec.whatwg.org/#host-parsing
512
+ function parseHost (host, isNotSpecial) {
513
+ return host
514
+ }
515
+
516
+ // https://url.spec.whatwg.org/#special-scheme
517
+ function isSpecialScheme (scheme) {
518
+ switch (scheme) {
519
+ case 'ftp':
520
+ case 'file':
521
+ case 'http':
522
+ case 'https':
523
+ case 'ws':
524
+ case 'wss':
525
+ return true
526
+ default:
527
+ return false
528
+ }
529
+ }
530
+
531
+ // https://url.spec.whatwg.org/#default-port
532
+ function defaultPort (scheme) {
533
+ switch (scheme) {
534
+ case 'ftp': return 21
535
+ case 'file': return null
536
+ case 'http': return 80
537
+ case 'https': return 443
538
+ case 'ws': return 80
539
+ case 'wss': return 443
540
+ default: return null
541
+ }
542
+ }
543
+
544
+ // https://url.spec.whatwg.org/#is-special
545
+ function isSpecial (url) {
546
+ return isSpecialScheme(url)
547
+ }
548
+
549
+ // https://url.spec.whatwg.org/#include-credentials
550
+ function includesCredentials (url) {
551
+ return url.username !== '' && url.password !== ''
552
+ }
553
+
554
+ // https://url.spec.whatwg.org/#url-opaque-path
555
+ function hasOpaquePath (url) {
556
+ return typeof url.path === 'string'
557
+ }
558
+
559
+ // https://url.spec.whatwg.org/#windows-drive-letter
560
+ function isWindowsDriveLetter (input) {
561
+ return input.length >= 2 && infra.isASCIIAlpha(input.charCodeAt(0)) && (
562
+ input.charCodeAt(1) === 0x3a ||
563
+ input.charCodeAt(1) === 0x7c
564
+ )
565
+ }
566
+
567
+ // https://url.spec.whatwg.org/#normalized-windows-drive-letter
568
+ function isNormalizedWindowsDriveLetter (input) {
569
+ return input.length >= 2 && infra.isASCIIAlpha(input.charCodeAt(0)) && input.charCodeAt(1) === 0x3a
570
+ }
571
+
572
+ // https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
573
+ function startsWithWindowsDriveLetter (input) {
574
+ return input.length >= 2 && isWindowsDriveLetter(input) && (
575
+ input.length === 2 ||
576
+ input.charCodeAt(2) === 0x2f ||
577
+ input.charCodeAt(2) === 0x5c ||
578
+ input.charCodeAt(2) === 0x3f ||
579
+ input.charCodeAt(2) === 0x23
580
+ )
581
+ }
582
+
583
+ // https://url.spec.whatwg.org/#shorten-a-urls-path
584
+ function shortenPath (url) {
585
+ const path = url.path
586
+
587
+ if (url.scheme === 'file' && path.length === 1 && isNormalizedWindowsDriveLetter(path[0])) {
588
+ return
589
+ }
590
+
591
+ path.pop()
592
+ }
593
+
594
+ // https://url.spec.whatwg.org/#single-dot-path-segment
595
+ function isSingleDotPathSegment (segment) {
596
+ return segment === '.' || decodeURIComponent(segment) === '.'
597
+ }
598
+
599
+ // https://url.spec.whatwg.org/#double-dot-path-segment
600
+ function isDoubleDotPathSegment (segment) {
601
+ return segment === '..' || decodeURIComponent(segment) === '..'
602
+ }
@@ -0,0 +1,49 @@
1
+ // https://url.spec.whatwg.org/#url-serializing
2
+ module.exports = function serialize (url, excludeFragment = false) {
3
+ let output = url.scheme + ':'
4
+
5
+ if (url.host) {
6
+ output += '//'
7
+
8
+ if (url.username !== '' || url.password !== '') {
9
+ output += url.username
10
+
11
+ if (url.password !== '') {
12
+ output += ':' + url.password
13
+ }
14
+
15
+ output += '@'
16
+ }
17
+
18
+ output += url.host
19
+
20
+ if (url.port !== null) {
21
+ output += ':' + url.port.toString(10)
22
+ }
23
+ }
24
+
25
+ if (
26
+ url.host === null &&
27
+ typeof url.path !== 'string' &&
28
+ url.path.length > 1 &&
29
+ url.path[0] === ''
30
+ ) {
31
+ output += '/.'
32
+ }
33
+
34
+ if (typeof url.path === 'string') {
35
+ output += url.path
36
+ } else {
37
+ output += '/' + url.path.join('/')
38
+ }
39
+
40
+ if (url.query !== null) {
41
+ output += '?' + url.query
42
+ }
43
+
44
+ if (url.fragment !== null && !excludeFragment) {
45
+ output += '#' + url.fragment
46
+ }
47
+
48
+ return output
49
+ }
package/package.json CHANGED
@@ -1,23 +1,27 @@
1
1
  {
2
2
  "name": "bare-url",
3
- "version": "0.0.0",
4
- "description": "URL parser for Javascript",
3
+ "version": "0.1.1",
4
+ "description": "URL parser for JavaScript",
5
5
  "main": "index.js",
6
+ "files": [
7
+ "index.js",
8
+ "lib"
9
+ ],
6
10
  "scripts": {
7
- "test": "standard && brittle test.js"
8
- },
9
- "devDependencies": {
10
- "standard": "^17.1.0",
11
- "brittle": "^3.3.2"
11
+ "test": "standard && bare test.js"
12
12
  },
13
13
  "repository": {
14
14
  "type": "git",
15
- "url": "https://github.com/holepunchto/bare-url.git"
15
+ "url": "git+https://github.com/holepunchto/bare-url.git"
16
16
  },
17
17
  "author": "Holepunch",
18
18
  "license": "Apache-2.0",
19
19
  "bugs": {
20
20
  "url": "https://github.com/holepunchto/bare-url/issues"
21
21
  },
22
- "homepage": "https://github.com/holepunchto/bare-url"
22
+ "homepage": "https://github.com/holepunchto/bare-url",
23
+ "devDependencies": {
24
+ "brittle": "^3.3.2",
25
+ "standard": "^17.1.0"
26
+ }
23
27
  }
@@ -1,23 +0,0 @@
1
- name: Build Status
2
- on:
3
- push:
4
- branches:
5
- - main
6
- pull_request:
7
- branches:
8
- - main
9
- jobs:
10
- build:
11
- strategy:
12
- matrix:
13
- node-version: [lts/*]
14
- os: [ubuntu-latest, macos-latest, windows-latest]
15
- runs-on: ${{ matrix.os }}
16
- steps:
17
- - uses: actions/checkout@v2
18
- - name: Use Node.js ${{ matrix.node-version }}
19
- uses: actions/setup-node@v2
20
- with:
21
- node-version: ${{ matrix.node-version }}
22
- - run: npm install
23
- - run: npm test
package/NOTICE DELETED
@@ -1,13 +0,0 @@
1
- Copyright 2023 Holepunch Inc
2
-
3
- Licensed under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License.
5
- You may obtain a copy of the License at
6
-
7
- http://www.apache.org/licenses/LICENSE-2.0
8
-
9
- Unless required by applicable law or agreed to in writing, software
10
- distributed under the License is distributed on an "AS IS" BASIS,
11
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- See the License for the specific language governing permissions and
13
- limitations under the License.
package/test.js DELETED
@@ -1,6 +0,0 @@
1
- const test = require('brittle')
2
- const url = require('./')
3
-
4
- test('fileURLToPath', function (t) {
5
- t.is(url.fileURLToPath('file:///foo/bar'), process.platform === 'win32' ? '\\foo\\bar' : '/foo/bar')
6
- })