-
-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
distribute ESM version #50
Comments
Yeah, I suppose we ought to do that. Fair warning, though: I just went through a similar transition with a different project, and was more work than it sounded on the surface. The main thing I want is to keep backwards compatibility for existing users, so keep a CJS version and no I suspect that the best way to do it is probably a thin ESM wrapper that just re-exports the existing CJS module and provides a named and/or default export. |
@nfriedly Hi 👋 would you consider adding a small script to generate the ESM version? It doesn't add dependencies or anything, it just concatenates the CJS version with an extra bit of code in an |
Hum, that's an interesting idea, I like the simplicity. What I had been thinking about was converting it to typescript, and then having tsc run twice to output both esm and cjs versions of the library, but I've done that with a few other libraries and it's a decent amount of work each time. If you want to send in a PR to add the conversion script, I'll go ahead and merge it in, and I'll punt typescript for some other day (or maybe never). |
A couple of thoughts if we go with a generation script:
|
I also wonder if it should go in the opposite direction. Start with esm source and then generate the cjs version. Esm will be around longer-term and eventually everyone will be dropping cjs. That would make it easier for this library in the future as then you can just drop the generation script when the time comes and don't have to update the source |
Yeah, that makes sense. |
Here's the ESM version in case anyone still needs this. I was having trouble bundling this package with Vite and decided to copy-paste code and transform it to Typescript. File: types.ts export type ParseOptions = {
/**
* Calls decodeURIComponent on each value
* @default true
*/
decodeValues?: boolean;
/**
* Return an object instead of an array
* @default false
*/
map?: boolean;
/**
* Suppress the warning that is loged when called on a request instead of a response
* @default false
*/
silent?: boolean;
};
export interface ParsedCookie {
name: string;
/**
* cookie value
*/
value: string;
/**
* cookie path
*/
path?: string;
/**
* absolute expiration date for the cookie
*/
expires?: Date;
/**
* relative max age of the cookie in seconds from when the client receives it (integer or undefined)
* Note: when using with express's res.cookie() method, multiply maxAge by 1000 to convert to milliseconds
*/
maxAge?: number;
/**
* Domain for the cookie, may begin with "." to indicate the named
* domain or any subdomain of it
*/
domain?: string;
/**
* indicates that this cookie should only be sent over HTTPs
*/
secure?: boolean;
/**
* indicates that this cookie should not be accessible to client-side JavaScript
*/
httpOnly?: boolean;
/**
* indicates a cookie ought not to be sent along with cross-site requests
*/
sameSite?: string;
}
export interface ParsedCookieMap {
[name: string]: ParsedCookie;
} file: cookies.util.ts import type { ParsedCookie, ParsedCookieMap, ParseOptions } from './types';
export class CookiesUtil {
private static readonly defaultParseOptions: ParseOptions = {
decodeValues: true,
map: false,
silent: false
};
static parse(
input: string | string[] | undefined,
options: ParseOptions & { map: true }
): ParsedCookieMap;
static parse(
input: string | string[] | undefined,
options?: ParseOptions & { map?: false | undefined }
): ParsedCookie[];
static parse(
input: string | string[] | undefined,
options?: ParseOptions
): ParsedCookie[] | ParsedCookieMap {
options = { ...this.defaultParseOptions, ...(options ?? {}) };
if (!input) {
return options.map ? {} : [];
}
if (!Array.isArray(input)) {
input = [input];
}
if (!options.map) {
return input
.filter((part) => part.trim().length)
.map((str) => this.parseString(str, options));
}
const cookies: Record<string, ParsedCookie> = {};
return input
.filter((part) => part.trim().length)
.reduce((cookies, str) => {
const cookie = this.parseString(str, options);
cookies[cookie.name] = cookie;
return cookies;
}, cookies);
}
static parseString(
setCookieValue: string,
options?: ParseOptions
): ParsedCookie {
options = { ...this.defaultParseOptions, ...(options ?? {}) };
const parts = setCookieValue
.split(';')
.filter((part) => part.trim().length);
const nameValuePairStr = parts.shift()!;
const parsed = this.__parseNameValuePair(nameValuePairStr);
const { name, value: initialValue } = parsed;
let value = initialValue;
try {
value = options.decodeValues ? decodeURIComponent(value) : value;
} catch (error) {
console.error(
`Encountered an error while decoding a cookie with value '${value}'. Set options.decodeValues to false to disable this feature.`,
error
);
}
const cookie: ParsedCookie = { name, value };
parts.forEach((part) => {
const sides = part.split('=');
const key = sides.shift()!.trimStart().toLowerCase();
const val = sides.join('=');
if (key === 'expires') {
cookie.expires = new Date(val);
} else if (key === 'max-age') {
cookie.maxAge = parseInt(val, 10);
} else if (key === 'secure') {
cookie.secure = true;
} else if (key === 'httponly') {
cookie.httpOnly = true;
} else if (key === 'samesite') {
cookie.sameSite = val;
} else {
cookie[key] = val;
}
});
return cookie;
}
static splitCookiesString(cookiesString: string | string[]): string[] {
if (Array.isArray(cookiesString)) {
return cookiesString;
}
const cookiesStrings: string[] = [];
let pos = 0;
let start: number;
let ch: string;
let lastComma: number;
let nextStart: number;
let cookiesSeparatorFound: boolean;
const skipWhitespace = (): boolean => {
while (
pos < cookiesString.length &&
/\s/.test(cookiesString.charAt(pos))
) {
pos += 1;
}
return pos < cookiesString.length;
};
const notSpecialChar = (): boolean => {
ch = cookiesString.charAt(pos);
return ch !== '=' && ch !== ';' && ch !== ',';
};
while (pos < cookiesString.length) {
start = pos;
cookiesSeparatorFound = false;
while (skipWhitespace()) {
ch = cookiesString.charAt(pos);
if (ch === ',') {
lastComma = pos;
pos += 1;
skipWhitespace();
nextStart = pos;
while (pos < cookiesString.length && notSpecialChar()) {
pos += 1;
}
if (
pos < cookiesString.length &&
cookiesString.charAt(pos) === '='
) {
cookiesSeparatorFound = true;
pos = nextStart;
cookiesStrings.push(
cookiesString.substring(start, lastComma)
);
start = pos;
} else {
pos = lastComma + 1;
}
} else {
pos += 1;
}
}
if (!cookiesSeparatorFound || pos >= cookiesString.length) {
cookiesStrings.push(cookiesString.substring(start));
}
}
return cookiesStrings;
}
private static __parseNameValuePair(nameValuePairStr: string): {
name: string;
value: string;
} {
const nameValueArr = nameValuePairStr.split('=');
const name = nameValueArr.shift()!;
const value = nameValueArr.join('=');
return { name, value };
}
} |
I was wondering if you'd be open to a PR to convert to ESM and what your thoughts are about ESM vs CJS, etc.
The text was updated successfully, but these errors were encountered: