From f419e27770a76c67e25783fc1a178bdcbde617d2 Mon Sep 17 00:00:00 2001 From: Paul Kiddle Date: Tue, 17 Oct 2023 17:43:29 +0100 Subject: [PATCH 1/2] Add ESM build script --- .gitignore | 1 + package.json | 9 ++++++++- scripts/build-esm.js | 28 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 scripts/build-esm.js diff --git a/.gitignore b/.gitignore index f15b98e..569a5c0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ coverage/ node_modules/ npm-debug.log package-lock.json +index.mjs diff --git a/package.json b/package.json index 9d69c8c..ccc0bf5 100644 --- a/package.json +++ b/package.json @@ -27,18 +27,25 @@ "LICENSE", "README.md", "SECURITY.md", - "index.js" + "index.js", + "index.mjs" ], "engines": { "node": ">= 0.6" }, "scripts": { "bench": "node benchmark/index.js", + "build-esm": "node scripts/build-esm.js", "lint": "eslint .", + "prepare": "npm run build-esm", "test": "mocha --reporter spec --bail --check-leaks test/", "test-ci": "nyc --reporter=lcov --reporter=text npm test", "test-cov": "nyc --reporter=html --reporter=text npm test", "update-bench": "node scripts/update-benchmark.js", "version": "node scripts/version-history.js && git add HISTORY.md" + }, + "exports": { + "import": "./index.mjs", + "require": "./index.js" } } diff --git a/scripts/build-esm.js b/scripts/build-esm.js new file mode 100644 index 0000000..198321a --- /dev/null +++ b/scripts/build-esm.js @@ -0,0 +1,28 @@ +'use strict' + +/* Generate the ESM version of the library by prepending an export statement + * Makes the following assumptions about index.js: + * - that `parse` and `serialize` are functions that get hoisted + * - the CJS file references `exports`, not `module.exports` + * + * Although modules use strict mode by default, + * this script does not remove the 'use strict' pragma. + */ + +var fs = require('fs') +var path = require('path') + +var srcFile = path.join(__dirname, '../index.js') +var destFile = path.join(__dirname, '../index.mjs') + +var cjsCode = fs.readFileSync(srcFile, 'utf-8') +var esmCode = `const exports = {} +export { + parse, + serialize, + exports as default +} + +${cjsCode}` + +fs.writeFileSync(destFile, esmCode); From e7290182a0347a9f7885d7009a321f4d41cf701e Mon Sep 17 00:00:00 2001 From: Paul Kiddle Date: Mon, 23 Oct 2023 13:40:59 +0100 Subject: [PATCH 2/2] Fix lint failures, commit generated esm file --- .gitignore | 1 - index.mjs | 277 +++++++++++++++++++++++++++++++++++++++++++ scripts/build-esm.js | 15 ++- 3 files changed, 284 insertions(+), 9 deletions(-) create mode 100644 index.mjs diff --git a/.gitignore b/.gitignore index 569a5c0..f15b98e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ coverage/ node_modules/ npm-debug.log package-lock.json -index.mjs diff --git a/index.mjs b/index.mjs new file mode 100644 index 0000000..40862fb --- /dev/null +++ b/index.mjs @@ -0,0 +1,277 @@ +const exports = {} +export { + parse, + serialize, + exports as default +} + +/*! + * cookie + * Copyright(c) 2012-2014 Roman Shtylman + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict'; + +/** + * Module exports. + * @public + */ + +exports.parse = parse; +exports.serialize = serialize; + +/** + * Module variables. + * @private + */ + +var __toString = Object.prototype.toString + +/** + * RegExp to match field-content in RFC 7230 sec 3.2 + * + * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] + * field-vchar = VCHAR / obs-text + * obs-text = %x80-FF + */ + +var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/; + +/** + * Parse a cookie header. + * + * Parse the given cookie header string into an object + * The object has the various cookies as keys(names) => values + * + * @param {string} str + * @param {object} [options] + * @return {object} + * @public + */ + +function parse(str, options) { + if (typeof str !== 'string') { + throw new TypeError('argument str must be a string'); + } + + var obj = {} + var opt = options || {}; + var dec = opt.decode || decode; + + var index = 0 + while (index < str.length) { + var eqIdx = str.indexOf('=', index) + + // no more cookie pairs + if (eqIdx === -1) { + break + } + + var endIdx = str.indexOf(';', index) + + if (endIdx === -1) { + endIdx = str.length + } else if (endIdx < eqIdx) { + // backtrack on prior semicolon + index = str.lastIndexOf(';', eqIdx - 1) + 1 + continue + } + + var key = str.slice(index, eqIdx).trim() + + // only assign once + if (undefined === obj[key]) { + var val = str.slice(eqIdx + 1, endIdx).trim() + + // quoted values + if (val.charCodeAt(0) === 0x22) { + val = val.slice(1, -1) + } + + obj[key] = tryDecode(val, dec); + } + + index = endIdx + 1 + } + + return obj; +} + +/** + * Serialize data into a cookie header. + * + * Serialize the a name value pair into a cookie string suitable for + * http headers. An optional options object specified cookie parameters. + * + * serialize('foo', 'bar', { httpOnly: true }) + * => "foo=bar; httpOnly" + * + * @param {string} name + * @param {string} val + * @param {object} [options] + * @return {string} + * @public + */ + +function serialize(name, val, options) { + var opt = options || {}; + var enc = opt.encode || encode; + + if (typeof enc !== 'function') { + throw new TypeError('option encode is invalid'); + } + + if (!fieldContentRegExp.test(name)) { + throw new TypeError('argument name is invalid'); + } + + var value = enc(val); + + if (value && !fieldContentRegExp.test(value)) { + throw new TypeError('argument val is invalid'); + } + + var str = name + '=' + value; + + if (null != opt.maxAge) { + var maxAge = opt.maxAge - 0; + + if (isNaN(maxAge) || !isFinite(maxAge)) { + throw new TypeError('option maxAge is invalid') + } + + str += '; Max-Age=' + Math.floor(maxAge); + } + + if (opt.domain) { + if (!fieldContentRegExp.test(opt.domain)) { + throw new TypeError('option domain is invalid'); + } + + str += '; Domain=' + opt.domain; + } + + if (opt.path) { + if (!fieldContentRegExp.test(opt.path)) { + throw new TypeError('option path is invalid'); + } + + str += '; Path=' + opt.path; + } + + if (opt.expires) { + var expires = opt.expires + + if (!isDate(expires) || isNaN(expires.valueOf())) { + throw new TypeError('option expires is invalid'); + } + + str += '; Expires=' + expires.toUTCString() + } + + if (opt.httpOnly) { + str += '; HttpOnly'; + } + + if (opt.secure) { + str += '; Secure'; + } + + if (opt.priority) { + var priority = typeof opt.priority === 'string' + ? opt.priority.toLowerCase() + : opt.priority + + switch (priority) { + case 'low': + str += '; Priority=Low' + break + case 'medium': + str += '; Priority=Medium' + break + case 'high': + str += '; Priority=High' + break + default: + throw new TypeError('option priority is invalid') + } + } + + if (opt.sameSite) { + var sameSite = typeof opt.sameSite === 'string' + ? opt.sameSite.toLowerCase() : opt.sameSite; + + switch (sameSite) { + case true: + str += '; SameSite=Strict'; + break; + case 'lax': + str += '; SameSite=Lax'; + break; + case 'strict': + str += '; SameSite=Strict'; + break; + case 'none': + str += '; SameSite=None'; + break; + default: + throw new TypeError('option sameSite is invalid'); + } + } + + return str; +} + +/** + * URL-decode string value. Optimized to skip native call when no %. + * + * @param {string} str + * @returns {string} + */ + +function decode (str) { + return str.indexOf('%') !== -1 + ? decodeURIComponent(str) + : str +} + +/** + * URL-encode value. + * + * @param {string} str + * @returns {string} + */ + +function encode (val) { + return encodeURIComponent(val) +} + +/** + * Determine if value is a Date. + * + * @param {*} val + * @private + */ + +function isDate (val) { + return __toString.call(val) === '[object Date]' || + val instanceof Date +} + +/** + * Try decoding a string using a decoding function. + * + * @param {string} str + * @param {function} decode + * @private + */ + +function tryDecode(str, decode) { + try { + return decode(str); + } catch (e) { + return str; + } +} diff --git a/scripts/build-esm.js b/scripts/build-esm.js index 198321a..ccb4fd2 100644 --- a/scripts/build-esm.js +++ b/scripts/build-esm.js @@ -16,13 +16,12 @@ var srcFile = path.join(__dirname, '../index.js') var destFile = path.join(__dirname, '../index.mjs') var cjsCode = fs.readFileSync(srcFile, 'utf-8') -var esmCode = `const exports = {} -export { - parse, - serialize, - exports as default -} - -${cjsCode}` +var esmCode = "const exports = {}\ +export {\ + parse,\ + serialize,\ + exports as default\ +}\ +" + cjsCode; fs.writeFileSync(destFile, esmCode);