data-url.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. 'use strict'
  2. const assert = require('node:assert')
  3. const encoder = new TextEncoder()
  4. /**
  5. * @see https://mimesniff.spec.whatwg.org/#http-token-code-point
  6. */
  7. const HTTP_TOKEN_CODEPOINTS = /^[!#$%&'*+\-.^_|~A-Za-z0-9]+$/
  8. const HTTP_WHITESPACE_REGEX = /[\u000A\u000D\u0009\u0020]/ // eslint-disable-line
  9. const ASCII_WHITESPACE_REPLACE_REGEX = /[\u0009\u000A\u000C\u000D\u0020]/g // eslint-disable-line
  10. /**
  11. * @see https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point
  12. */
  13. const HTTP_QUOTED_STRING_TOKENS = /^[\u0009\u0020-\u007E\u0080-\u00FF]+$/ // eslint-disable-line
  14. // https://fetch.spec.whatwg.org/#data-url-processor
  15. /** @param {URL} dataURL */
  16. function dataURLProcessor (dataURL) {
  17. // 1. Assert: dataURL’s scheme is "data".
  18. assert(dataURL.protocol === 'data:')
  19. // 2. Let input be the result of running the URL
  20. // serializer on dataURL with exclude fragment
  21. // set to true.
  22. let input = URLSerializer(dataURL, true)
  23. // 3. Remove the leading "data:" string from input.
  24. input = input.slice(5)
  25. // 4. Let position point at the start of input.
  26. const position = { position: 0 }
  27. // 5. Let mimeType be the result of collecting a
  28. // sequence of code points that are not equal
  29. // to U+002C (,), given position.
  30. let mimeType = collectASequenceOfCodePointsFast(
  31. ',',
  32. input,
  33. position
  34. )
  35. // 6. Strip leading and trailing ASCII whitespace
  36. // from mimeType.
  37. // Undici implementation note: we need to store the
  38. // length because if the mimetype has spaces removed,
  39. // the wrong amount will be sliced from the input in
  40. // step #9
  41. const mimeTypeLength = mimeType.length
  42. mimeType = removeASCIIWhitespace(mimeType, true, true)
  43. // 7. If position is past the end of input, then
  44. // return failure
  45. if (position.position >= input.length) {
  46. return 'failure'
  47. }
  48. // 8. Advance position by 1.
  49. position.position++
  50. // 9. Let encodedBody be the remainder of input.
  51. const encodedBody = input.slice(mimeTypeLength + 1)
  52. // 10. Let body be the percent-decoding of encodedBody.
  53. let body = stringPercentDecode(encodedBody)
  54. // 11. If mimeType ends with U+003B (;), followed by
  55. // zero or more U+0020 SPACE, followed by an ASCII
  56. // case-insensitive match for "base64", then:
  57. if (/;(\u0020){0,}base64$/i.test(mimeType)) {
  58. // 1. Let stringBody be the isomorphic decode of body.
  59. const stringBody = isomorphicDecode(body)
  60. // 2. Set body to the forgiving-base64 decode of
  61. // stringBody.
  62. body = forgivingBase64(stringBody)
  63. // 3. If body is failure, then return failure.
  64. if (body === 'failure') {
  65. return 'failure'
  66. }
  67. // 4. Remove the last 6 code points from mimeType.
  68. mimeType = mimeType.slice(0, -6)
  69. // 5. Remove trailing U+0020 SPACE code points from mimeType,
  70. // if any.
  71. mimeType = mimeType.replace(/(\u0020)+$/, '')
  72. // 6. Remove the last U+003B (;) code point from mimeType.
  73. mimeType = mimeType.slice(0, -1)
  74. }
  75. // 12. If mimeType starts with U+003B (;), then prepend
  76. // "text/plain" to mimeType.
  77. if (mimeType.startsWith(';')) {
  78. mimeType = 'text/plain' + mimeType
  79. }
  80. // 13. Let mimeTypeRecord be the result of parsing
  81. // mimeType.
  82. let mimeTypeRecord = parseMIMEType(mimeType)
  83. // 14. If mimeTypeRecord is failure, then set
  84. // mimeTypeRecord to text/plain;charset=US-ASCII.
  85. if (mimeTypeRecord === 'failure') {
  86. mimeTypeRecord = parseMIMEType('text/plain;charset=US-ASCII')
  87. }
  88. // 15. Return a new data: URL struct whose MIME
  89. // type is mimeTypeRecord and body is body.
  90. // https://fetch.spec.whatwg.org/#data-url-struct
  91. return { mimeType: mimeTypeRecord, body }
  92. }
  93. // https://url.spec.whatwg.org/#concept-url-serializer
  94. /**
  95. * @param {URL} url
  96. * @param {boolean} excludeFragment
  97. */
  98. function URLSerializer (url, excludeFragment = false) {
  99. if (!excludeFragment) {
  100. return url.href
  101. }
  102. const href = url.href
  103. const hashLength = url.hash.length
  104. const serialized = hashLength === 0 ? href : href.substring(0, href.length - hashLength)
  105. if (!hashLength && href.endsWith('#')) {
  106. return serialized.slice(0, -1)
  107. }
  108. return serialized
  109. }
  110. // https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
  111. /**
  112. * @param {(char: string) => boolean} condition
  113. * @param {string} input
  114. * @param {{ position: number }} position
  115. */
  116. function collectASequenceOfCodePoints (condition, input, position) {
  117. // 1. Let result be the empty string.
  118. let result = ''
  119. // 2. While position doesn’t point past the end of input and the
  120. // code point at position within input meets the condition condition:
  121. while (position.position < input.length && condition(input[position.position])) {
  122. // 1. Append that code point to the end of result.
  123. result += input[position.position]
  124. // 2. Advance position by 1.
  125. position.position++
  126. }
  127. // 3. Return result.
  128. return result
  129. }
  130. /**
  131. * A faster collectASequenceOfCodePoints that only works when comparing a single character.
  132. * @param {string} char
  133. * @param {string} input
  134. * @param {{ position: number }} position
  135. */
  136. function collectASequenceOfCodePointsFast (char, input, position) {
  137. const idx = input.indexOf(char, position.position)
  138. const start = position.position
  139. if (idx === -1) {
  140. position.position = input.length
  141. return input.slice(start)
  142. }
  143. position.position = idx
  144. return input.slice(start, position.position)
  145. }
  146. // https://url.spec.whatwg.org/#string-percent-decode
  147. /** @param {string} input */
  148. function stringPercentDecode (input) {
  149. // 1. Let bytes be the UTF-8 encoding of input.
  150. const bytes = encoder.encode(input)
  151. // 2. Return the percent-decoding of bytes.
  152. return percentDecode(bytes)
  153. }
  154. /**
  155. * @param {number} byte
  156. */
  157. function isHexCharByte (byte) {
  158. // 0-9 A-F a-f
  159. return (byte >= 0x30 && byte <= 0x39) || (byte >= 0x41 && byte <= 0x46) || (byte >= 0x61 && byte <= 0x66)
  160. }
  161. /**
  162. * @param {number} byte
  163. */
  164. function hexByteToNumber (byte) {
  165. return (
  166. // 0-9
  167. byte >= 0x30 && byte <= 0x39
  168. ? (byte - 48)
  169. // Convert to uppercase
  170. // ((byte & 0xDF) - 65) + 10
  171. : ((byte & 0xDF) - 55)
  172. )
  173. }
  174. // https://url.spec.whatwg.org/#percent-decode
  175. /** @param {Uint8Array} input */
  176. function percentDecode (input) {
  177. const length = input.length
  178. // 1. Let output be an empty byte sequence.
  179. /** @type {Uint8Array} */
  180. const output = new Uint8Array(length)
  181. let j = 0
  182. // 2. For each byte byte in input:
  183. for (let i = 0; i < length; ++i) {
  184. const byte = input[i]
  185. // 1. If byte is not 0x25 (%), then append byte to output.
  186. if (byte !== 0x25) {
  187. output[j++] = byte
  188. // 2. Otherwise, if byte is 0x25 (%) and the next two bytes
  189. // after byte in input are not in the ranges
  190. // 0x30 (0) to 0x39 (9), 0x41 (A) to 0x46 (F),
  191. // and 0x61 (a) to 0x66 (f), all inclusive, append byte
  192. // to output.
  193. } else if (
  194. byte === 0x25 &&
  195. !(isHexCharByte(input[i + 1]) && isHexCharByte(input[i + 2]))
  196. ) {
  197. output[j++] = 0x25
  198. // 3. Otherwise:
  199. } else {
  200. // 1. Let bytePoint be the two bytes after byte in input,
  201. // decoded, and then interpreted as hexadecimal number.
  202. // 2. Append a byte whose value is bytePoint to output.
  203. output[j++] = (hexByteToNumber(input[i + 1]) << 4) | hexByteToNumber(input[i + 2])
  204. // 3. Skip the next two bytes in input.
  205. i += 2
  206. }
  207. }
  208. // 3. Return output.
  209. return length === j ? output : output.subarray(0, j)
  210. }
  211. // https://mimesniff.spec.whatwg.org/#parse-a-mime-type
  212. /** @param {string} input */
  213. function parseMIMEType (input) {
  214. // 1. Remove any leading and trailing HTTP whitespace
  215. // from input.
  216. input = removeHTTPWhitespace(input, true, true)
  217. // 2. Let position be a position variable for input,
  218. // initially pointing at the start of input.
  219. const position = { position: 0 }
  220. // 3. Let type be the result of collecting a sequence
  221. // of code points that are not U+002F (/) from
  222. // input, given position.
  223. const type = collectASequenceOfCodePointsFast(
  224. '/',
  225. input,
  226. position
  227. )
  228. // 4. If type is the empty string or does not solely
  229. // contain HTTP token code points, then return failure.
  230. // https://mimesniff.spec.whatwg.org/#http-token-code-point
  231. if (type.length === 0 || !HTTP_TOKEN_CODEPOINTS.test(type)) {
  232. return 'failure'
  233. }
  234. // 5. If position is past the end of input, then return
  235. // failure
  236. if (position.position > input.length) {
  237. return 'failure'
  238. }
  239. // 6. Advance position by 1. (This skips past U+002F (/).)
  240. position.position++
  241. // 7. Let subtype be the result of collecting a sequence of
  242. // code points that are not U+003B (;) from input, given
  243. // position.
  244. let subtype = collectASequenceOfCodePointsFast(
  245. ';',
  246. input,
  247. position
  248. )
  249. // 8. Remove any trailing HTTP whitespace from subtype.
  250. subtype = removeHTTPWhitespace(subtype, false, true)
  251. // 9. If subtype is the empty string or does not solely
  252. // contain HTTP token code points, then return failure.
  253. if (subtype.length === 0 || !HTTP_TOKEN_CODEPOINTS.test(subtype)) {
  254. return 'failure'
  255. }
  256. const typeLowercase = type.toLowerCase()
  257. const subtypeLowercase = subtype.toLowerCase()
  258. // 10. Let mimeType be a new MIME type record whose type
  259. // is type, in ASCII lowercase, and subtype is subtype,
  260. // in ASCII lowercase.
  261. // https://mimesniff.spec.whatwg.org/#mime-type
  262. const mimeType = {
  263. type: typeLowercase,
  264. subtype: subtypeLowercase,
  265. /** @type {Map<string, string>} */
  266. parameters: new Map(),
  267. // https://mimesniff.spec.whatwg.org/#mime-type-essence
  268. essence: `${typeLowercase}/${subtypeLowercase}`
  269. }
  270. // 11. While position is not past the end of input:
  271. while (position.position < input.length) {
  272. // 1. Advance position by 1. (This skips past U+003B (;).)
  273. position.position++
  274. // 2. Collect a sequence of code points that are HTTP
  275. // whitespace from input given position.
  276. collectASequenceOfCodePoints(
  277. // https://fetch.spec.whatwg.org/#http-whitespace
  278. char => HTTP_WHITESPACE_REGEX.test(char),
  279. input,
  280. position
  281. )
  282. // 3. Let parameterName be the result of collecting a
  283. // sequence of code points that are not U+003B (;)
  284. // or U+003D (=) from input, given position.
  285. let parameterName = collectASequenceOfCodePoints(
  286. (char) => char !== ';' && char !== '=',
  287. input,
  288. position
  289. )
  290. // 4. Set parameterName to parameterName, in ASCII
  291. // lowercase.
  292. parameterName = parameterName.toLowerCase()
  293. // 5. If position is not past the end of input, then:
  294. if (position.position < input.length) {
  295. // 1. If the code point at position within input is
  296. // U+003B (;), then continue.
  297. if (input[position.position] === ';') {
  298. continue
  299. }
  300. // 2. Advance position by 1. (This skips past U+003D (=).)
  301. position.position++
  302. }
  303. // 6. If position is past the end of input, then break.
  304. if (position.position > input.length) {
  305. break
  306. }
  307. // 7. Let parameterValue be null.
  308. let parameterValue = null
  309. // 8. If the code point at position within input is
  310. // U+0022 ("), then:
  311. if (input[position.position] === '"') {
  312. // 1. Set parameterValue to the result of collecting
  313. // an HTTP quoted string from input, given position
  314. // and the extract-value flag.
  315. parameterValue = collectAnHTTPQuotedString(input, position, true)
  316. // 2. Collect a sequence of code points that are not
  317. // U+003B (;) from input, given position.
  318. collectASequenceOfCodePointsFast(
  319. ';',
  320. input,
  321. position
  322. )
  323. // 9. Otherwise:
  324. } else {
  325. // 1. Set parameterValue to the result of collecting
  326. // a sequence of code points that are not U+003B (;)
  327. // from input, given position.
  328. parameterValue = collectASequenceOfCodePointsFast(
  329. ';',
  330. input,
  331. position
  332. )
  333. // 2. Remove any trailing HTTP whitespace from parameterValue.
  334. parameterValue = removeHTTPWhitespace(parameterValue, false, true)
  335. // 3. If parameterValue is the empty string, then continue.
  336. if (parameterValue.length === 0) {
  337. continue
  338. }
  339. }
  340. // 10. If all of the following are true
  341. // - parameterName is not the empty string
  342. // - parameterName solely contains HTTP token code points
  343. // - parameterValue solely contains HTTP quoted-string token code points
  344. // - mimeType’s parameters[parameterName] does not exist
  345. // then set mimeType’s parameters[parameterName] to parameterValue.
  346. if (
  347. parameterName.length !== 0 &&
  348. HTTP_TOKEN_CODEPOINTS.test(parameterName) &&
  349. (parameterValue.length === 0 || HTTP_QUOTED_STRING_TOKENS.test(parameterValue)) &&
  350. !mimeType.parameters.has(parameterName)
  351. ) {
  352. mimeType.parameters.set(parameterName, parameterValue)
  353. }
  354. }
  355. // 12. Return mimeType.
  356. return mimeType
  357. }
  358. // https://infra.spec.whatwg.org/#forgiving-base64-decode
  359. /** @param {string} data */
  360. function forgivingBase64 (data) {
  361. // 1. Remove all ASCII whitespace from data.
  362. data = data.replace(ASCII_WHITESPACE_REPLACE_REGEX, '') // eslint-disable-line
  363. let dataLength = data.length
  364. // 2. If data’s code point length divides by 4 leaving
  365. // no remainder, then:
  366. if (dataLength % 4 === 0) {
  367. // 1. If data ends with one or two U+003D (=) code points,
  368. // then remove them from data.
  369. if (data.charCodeAt(dataLength - 1) === 0x003D) {
  370. --dataLength
  371. if (data.charCodeAt(dataLength - 1) === 0x003D) {
  372. --dataLength
  373. }
  374. }
  375. }
  376. // 3. If data’s code point length divides by 4 leaving
  377. // a remainder of 1, then return failure.
  378. if (dataLength % 4 === 1) {
  379. return 'failure'
  380. }
  381. // 4. If data contains a code point that is not one of
  382. // U+002B (+)
  383. // U+002F (/)
  384. // ASCII alphanumeric
  385. // then return failure.
  386. if (/[^+/0-9A-Za-z]/.test(data.length === dataLength ? data : data.substring(0, dataLength))) {
  387. return 'failure'
  388. }
  389. const buffer = Buffer.from(data, 'base64')
  390. return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength)
  391. }
  392. // https://fetch.spec.whatwg.org/#collect-an-http-quoted-string
  393. // tests: https://fetch.spec.whatwg.org/#example-http-quoted-string
  394. /**
  395. * @param {string} input
  396. * @param {{ position: number }} position
  397. * @param {boolean?} extractValue
  398. */
  399. function collectAnHTTPQuotedString (input, position, extractValue) {
  400. // 1. Let positionStart be position.
  401. const positionStart = position.position
  402. // 2. Let value be the empty string.
  403. let value = ''
  404. // 3. Assert: the code point at position within input
  405. // is U+0022 (").
  406. assert(input[position.position] === '"')
  407. // 4. Advance position by 1.
  408. position.position++
  409. // 5. While true:
  410. while (true) {
  411. // 1. Append the result of collecting a sequence of code points
  412. // that are not U+0022 (") or U+005C (\) from input, given
  413. // position, to value.
  414. value += collectASequenceOfCodePoints(
  415. (char) => char !== '"' && char !== '\\',
  416. input,
  417. position
  418. )
  419. // 2. If position is past the end of input, then break.
  420. if (position.position >= input.length) {
  421. break
  422. }
  423. // 3. Let quoteOrBackslash be the code point at position within
  424. // input.
  425. const quoteOrBackslash = input[position.position]
  426. // 4. Advance position by 1.
  427. position.position++
  428. // 5. If quoteOrBackslash is U+005C (\), then:
  429. if (quoteOrBackslash === '\\') {
  430. // 1. If position is past the end of input, then append
  431. // U+005C (\) to value and break.
  432. if (position.position >= input.length) {
  433. value += '\\'
  434. break
  435. }
  436. // 2. Append the code point at position within input to value.
  437. value += input[position.position]
  438. // 3. Advance position by 1.
  439. position.position++
  440. // 6. Otherwise:
  441. } else {
  442. // 1. Assert: quoteOrBackslash is U+0022 (").
  443. assert(quoteOrBackslash === '"')
  444. // 2. Break.
  445. break
  446. }
  447. }
  448. // 6. If the extract-value flag is set, then return value.
  449. if (extractValue) {
  450. return value
  451. }
  452. // 7. Return the code points from positionStart to position,
  453. // inclusive, within input.
  454. return input.slice(positionStart, position.position)
  455. }
  456. /**
  457. * @see https://mimesniff.spec.whatwg.org/#serialize-a-mime-type
  458. */
  459. function serializeAMimeType (mimeType) {
  460. assert(mimeType !== 'failure')
  461. const { parameters, essence } = mimeType
  462. // 1. Let serialization be the concatenation of mimeType’s
  463. // type, U+002F (/), and mimeType’s subtype.
  464. let serialization = essence
  465. // 2. For each name → value of mimeType’s parameters:
  466. for (let [name, value] of parameters.entries()) {
  467. // 1. Append U+003B (;) to serialization.
  468. serialization += ';'
  469. // 2. Append name to serialization.
  470. serialization += name
  471. // 3. Append U+003D (=) to serialization.
  472. serialization += '='
  473. // 4. If value does not solely contain HTTP token code
  474. // points or value is the empty string, then:
  475. if (!HTTP_TOKEN_CODEPOINTS.test(value)) {
  476. // 1. Precede each occurrence of U+0022 (") or
  477. // U+005C (\) in value with U+005C (\).
  478. value = value.replace(/(\\|")/g, '\\$1')
  479. // 2. Prepend U+0022 (") to value.
  480. value = '"' + value
  481. // 3. Append U+0022 (") to value.
  482. value += '"'
  483. }
  484. // 5. Append value to serialization.
  485. serialization += value
  486. }
  487. // 3. Return serialization.
  488. return serialization
  489. }
  490. /**
  491. * @see https://fetch.spec.whatwg.org/#http-whitespace
  492. * @param {number} char
  493. */
  494. function isHTTPWhiteSpace (char) {
  495. // "\r\n\t "
  496. return char === 0x00d || char === 0x00a || char === 0x009 || char === 0x020
  497. }
  498. /**
  499. * @see https://fetch.spec.whatwg.org/#http-whitespace
  500. * @param {string} str
  501. * @param {boolean} [leading=true]
  502. * @param {boolean} [trailing=true]
  503. */
  504. function removeHTTPWhitespace (str, leading = true, trailing = true) {
  505. return removeChars(str, leading, trailing, isHTTPWhiteSpace)
  506. }
  507. /**
  508. * @see https://infra.spec.whatwg.org/#ascii-whitespace
  509. * @param {number} char
  510. */
  511. function isASCIIWhitespace (char) {
  512. // "\r\n\t\f "
  513. return char === 0x00d || char === 0x00a || char === 0x009 || char === 0x00c || char === 0x020
  514. }
  515. /**
  516. * @see https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace
  517. * @param {string} str
  518. * @param {boolean} [leading=true]
  519. * @param {boolean} [trailing=true]
  520. */
  521. function removeASCIIWhitespace (str, leading = true, trailing = true) {
  522. return removeChars(str, leading, trailing, isASCIIWhitespace)
  523. }
  524. /**
  525. * @param {string} str
  526. * @param {boolean} leading
  527. * @param {boolean} trailing
  528. * @param {(charCode: number) => boolean} predicate
  529. * @returns
  530. */
  531. function removeChars (str, leading, trailing, predicate) {
  532. let lead = 0
  533. let trail = str.length - 1
  534. if (leading) {
  535. while (lead < str.length && predicate(str.charCodeAt(lead))) lead++
  536. }
  537. if (trailing) {
  538. while (trail > 0 && predicate(str.charCodeAt(trail))) trail--
  539. }
  540. return lead === 0 && trail === str.length - 1 ? str : str.slice(lead, trail + 1)
  541. }
  542. /**
  543. * @see https://infra.spec.whatwg.org/#isomorphic-decode
  544. * @param {Uint8Array} input
  545. * @returns {string}
  546. */
  547. function isomorphicDecode (input) {
  548. // 1. To isomorphic decode a byte sequence input, return a string whose code point
  549. // length is equal to input’s length and whose code points have the same values
  550. // as the values of input’s bytes, in the same order.
  551. const length = input.length
  552. if ((2 << 15) - 1 > length) {
  553. return String.fromCharCode.apply(null, input)
  554. }
  555. let result = ''; let i = 0
  556. let addition = (2 << 15) - 1
  557. while (i < length) {
  558. if (i + addition > length) {
  559. addition = length - i
  560. }
  561. result += String.fromCharCode.apply(null, input.subarray(i, i += addition))
  562. }
  563. return result
  564. }
  565. /**
  566. * @see https://mimesniff.spec.whatwg.org/#minimize-a-supported-mime-type
  567. * @param {Exclude<ReturnType<typeof parseMIMEType>, 'failure'>} mimeType
  568. */
  569. function minimizeSupportedMimeType (mimeType) {
  570. switch (mimeType.essence) {
  571. case 'application/ecmascript':
  572. case 'application/javascript':
  573. case 'application/x-ecmascript':
  574. case 'application/x-javascript':
  575. case 'text/ecmascript':
  576. case 'text/javascript':
  577. case 'text/javascript1.0':
  578. case 'text/javascript1.1':
  579. case 'text/javascript1.2':
  580. case 'text/javascript1.3':
  581. case 'text/javascript1.4':
  582. case 'text/javascript1.5':
  583. case 'text/jscript':
  584. case 'text/livescript':
  585. case 'text/x-ecmascript':
  586. case 'text/x-javascript':
  587. // 1. If mimeType is a JavaScript MIME type, then return "text/javascript".
  588. return 'text/javascript'
  589. case 'application/json':
  590. case 'text/json':
  591. // 2. If mimeType is a JSON MIME type, then return "application/json".
  592. return 'application/json'
  593. case 'image/svg+xml':
  594. // 3. If mimeType’s essence is "image/svg+xml", then return "image/svg+xml".
  595. return 'image/svg+xml'
  596. case 'text/xml':
  597. case 'application/xml':
  598. // 4. If mimeType is an XML MIME type, then return "application/xml".
  599. return 'application/xml'
  600. }
  601. // 2. If mimeType is a JSON MIME type, then return "application/json".
  602. if (mimeType.subtype.endsWith('+json')) {
  603. return 'application/json'
  604. }
  605. // 4. If mimeType is an XML MIME type, then return "application/xml".
  606. if (mimeType.subtype.endsWith('+xml')) {
  607. return 'application/xml'
  608. }
  609. // 5. If mimeType is supported by the user agent, then return mimeType’s essence.
  610. // Technically, node doesn't support any mimetypes.
  611. // 6. Return the empty string.
  612. return ''
  613. }
  614. module.exports = {
  615. dataURLProcessor,
  616. URLSerializer,
  617. collectASequenceOfCodePoints,
  618. collectASequenceOfCodePointsFast,
  619. stringPercentDecode,
  620. parseMIMEType,
  621. collectAnHTTPQuotedString,
  622. serializeAMimeType,
  623. removeChars,
  624. removeHTTPWhitespace,
  625. minimizeSupportedMimeType,
  626. HTTP_TOKEN_CODEPOINTS,
  627. isomorphicDecode
  628. }