rollup-plugin-realm-uri
Version:
Import deduplicated references to intrinsic values using realm: scheme URIs
458 lines (382 loc) • 11.8 kB
JavaScript
import fs from 'fs';
const IDENTIFIER = /^[\p{ID_Start}$_][\p{ID_Continue}$\u200C\u200D]*$/u;
function isIdentifier(value) {
return IDENTIFIER.test(value);
}
var CONTEXTS = new Map([
'ArrayIteratorPrototype',
'AsyncFromSyncIteratorPrototype',
'AsyncFunction',
'AsyncFunctionPrototype',
'AsyncGenerator',
'AsyncGeneratorFunction',
'AsyncGeneratorPrototype',
'AsyncIteratorPrototype',
'Generator',
'GeneratorFunction',
'GeneratorPrototype',
'IteratorPrototype',
'MapIteratorPrototype',
'RegExpStringIteratorPrototype',
'SetIteratorPrototype',
'StringIteratorPrototype',
'ThrowTypeError',
'TypedArray',
'TypedArrayPrototype',
'globalThis'
].map(name => [ name, toContextFileURL(name) ]));
function toContextFileURL(name) {
const slug = name.replace(/(?<=[a-z])(?=[A-Z])/g, '-').toLowerCase();
return new URL(`../src/runtime/context-${ slug }.mjs`, import.meta.url);
}
var WELL_KNOWN_SYMBOLS = new Set([
'asyncIterator',
'hasInstance',
'isConcatSpreadable',
'iterator',
'match',
'matchAll',
'replace',
'search',
'species',
'split',
'toPrimitive',
'toStringTag',
'unscopables'
]);
const
KEY_TYPE_GLOBAL_SYMBOL = Symbol('TYPE_GLOBAL_SYMBOL'),
KEY_TYPE_STRING = Symbol('TYPE_STRING'),
KEY_TYPE_WELL_KNOWN_SYMBOL = Symbol('TYPE_WELL_KNOWN_SYMBOL');
const
TARGET_DESCRIPTOR = Symbol('TARGET_DESCRIPTOR'),
TARGET_GET = Symbol('TARGET_GET'),
TARGET_SET = Symbol('TARGET_SET'),
TARGET_VALUE = Symbol('TARGET_VALUE');
const
TRANSFORM_BIND = Symbol('TRANSFORM_BIND'),
TRANSFORM_INVERT = Symbol('TRANSFORM_INVERT'),
TRANSFORM_NONE = Symbol('TRANSFORM_NONE');
function parseRealmURI(source) {
try {
const url = new URL(source);
const { contextSource, pathSource } = splitPathSource(url.pathname);
const context = parseContext(contextSource);
const path = [ ...parsePath(pathSource) ];
const target = getTarget(url.hash);
const transform = getTransform(url.searchParams);
if (target !== TARGET_VALUE && path.length === 0) {
throw new URIError(`Only #value target is valid with empty path`);
}
if (transform === TRANSFORM_BIND && path.length === 0) {
throw new URIError(`Bind transform cannot be used with an empty path`);
}
if (target === TARGET_DESCRIPTOR && transform !== TRANSFORM_NONE) {
throw new URIError(`Only transform=none is valid with #descriptor`);
}
return { context, path, target, transform };
} catch (err) {
err.message += ` (specifier ${ source })`;
throw err;
}
}
////////////////////////////////////////////////////////////////////////////////
const PATH_SEGMENT_COVER_GRAMMAR =
/(?:(?:^|\.)(?<ident>[^[.]+))|\[(?<special>.+)\]|(?<invalid>.+)/gusy;
const PATH_SEGMENT_SPECIAL_COVER_GRAMMAR =
/^(?<symbol>@@)?(?:(?:"(?<string>[^"]*)")|(?<badstring>".*)|(?<ident>.+))$/s;
const PATHNAME_COVER_GRAMMAR =
/^(?:(?<contextSource>[^/[]*)\/)?(?<pathSource>.*)$/s;
////////////////////////////////////////////////////////////////////////////////
function assertIsIdentifier(value) {
if (!isIdentifier(value)) {
throw new URIError(quoted`${ value } is not an identifier`);
}
}
function assertIsKnownContext(value) {
if (!CONTEXTS.has(value)) {
throw new URIError(quoted`${ value } is not a recognized context name`);
}
}
function assertIsKnownSymbol(value) {
if (!WELL_KNOWN_SYMBOLS.has(value)) {
throw new URIError(quoted`${ value } is not a recognized symbol name`);
}
}
function assertIsNotAFSIP(value) {
if (value === 'AsyncFromSyncIteratorPrototype') {
throw new URIError(
`%AsyncFromSyncIteratorPrototype% is not implemented as a reachable, ` +
`reified ES value in any engine presently. The spec will likely be ` +
`amended to reflect web reality, in which case it should no longer be ` +
`listed as a well-known intrinsic object.`
);
}
}
function getTarget(fragment) {
switch (decodeURIComponent(fragment).toLowerCase()) {
case '#d': case '#descriptor':
return TARGET_DESCRIPTOR;
case '#g': case '#get':
return TARGET_GET;
case '#s': case '#set':
return TARGET_SET;
case '#v': case '#value': case '':
return TARGET_VALUE;
default:
throw new URIError(quoted`${ fragment } fragment is not valid`);
}
}
function getTransform(usp) {
let seen = false;
let transform = TRANSFORM_NONE;
for (const entry of usp) {
const [ key, value ] = entry.map(str => str.toLowerCase());
if (key !== 't' && key !== 'transform') {
throw new URIError(quoted`${ key } query parameter key is not valid`);
}
if (seen) {
throw new URIError('transform query parameter may appear only once');
}
seen = true;
switch (value) {
case 'b': case 'bind':
transform = TRANSFORM_BIND;
break;
case 'i': case 'invert':
transform = TRANSFORM_INVERT;
break;
case 'n': case 'none':
transform = TRANSFORM_NONE;
break;
default:
throw new URIError(quoted`${ value } transform is not valid`);
}
}
return transform;
}
function parseContext(source='') {
const context = decodeURIComponent(source) || 'globalThis';
assertIsIdentifier(context);
assertIsKnownContext(context);
assertIsNotAFSIP(context);
return context;
}
function * parsePath(source) {
for (const { groups } of source.matchAll(PATH_SEGMENT_COVER_GRAMMAR)) {
if (groups.ident) {
yield parsePathSegmentIdent(groups.ident);
} else if (groups.special) {
yield parsePathSegmentSpecial(groups.special);
} else {
throw new URIError(quoted`Realm URI malformed at ${ groups.invalid }`);
}
}
}
function parsePathSegmentIdent(source) {
const ident = decodeURIComponent(source);
assertIsIdentifier(ident);
return { type: KEY_TYPE_STRING, value: ident };
}
function parsePathSegmentSpecial(source) {
const { groups } = source.match(PATH_SEGMENT_SPECIAL_COVER_GRAMMAR);
if (groups.badstring) {
throw new URIError(quoted`Unterminated string in property ${ source }`);
}
if (groups.ident) {
if (!groups.symbol) {
throw new URIError(quoted`${ source } is not a valid bracketed property`);
}
const ident = decodeURIComponent(groups.ident);
assertIsIdentifier(ident);
assertIsKnownSymbol(ident);
return { type: KEY_TYPE_WELL_KNOWN_SYMBOL, value: ident };
}
return {
type: groups.symbol ? KEY_TYPE_GLOBAL_SYMBOL : KEY_TYPE_STRING,
value: decodeURIComponent(groups.string)
};
}
function quoted(template, ...substitutions) {
return String.raw(
{ raw: template },
...substitutions.map(value => JSON.stringify(value))
);
}
function splitPathSource(pathname) {
return pathname.match(PATHNAME_COVER_GRAMMAR).groups;
}
function renderRealmURI({ context, path, target, transform }) {
return new URL([
'realm:',
...renderContext(context),
...renderPath(path),
...renderQuery(transform),
...renderFragment(target)
].join('')).href;
}
function * renderContext(context) {
if (context !== 'globalThis') {
yield context;
yield '/';
}
}
function * renderFragment(target) {
switch (target) {
case TARGET_DESCRIPTOR:
yield '#d';
break;
case TARGET_GET:
yield '#g';
break;
case TARGET_SET:
yield '#s';
break;
}
}
function * renderQuery(transform) {
switch (transform) {
case TRANSFORM_BIND:
yield '?t=b';
break;
case TRANSFORM_INVERT:
yield '?t=i';
break;
}
}
function * renderPath(path) {
for (const [ index, segment ] of path.entries()) {
yield * renderPathSegment(segment, index);
}
}
function * renderPathSegment({ type, value }, index) {
if (type === KEY_TYPE_STRING && isIdentifier(value)) {
if (index !== 0) {
yield '.';
}
yield value;
} else {
yield '[';
if (type !== KEY_TYPE_STRING) {
yield '@@';
}
if (type === KEY_TYPE_WELL_KNOWN_SYMBOL) {
yield value;
} else {
yield `"${ value.replace(/[#"]/g, encodeURIComponent) }"`;
}
yield ']';
}
}
function canonicalizeRealmURI(uri) {
return renderRealmURI(parseRealmURI(uri));
}
function isRealmURI(specifier) {
try {
return new URL(specifier).protocol === 'realm:';
} catch {
return false;
}
}
var HELPERS = new Map([
'target-descriptor',
'target-get',
'target-set',
'transform-bind',
'transform-invert'
].map(name => [
`\0realm:${ name }`,
new URL(`../src/runtime/${ name }.mjs`, import.meta.url)
]));
function isRealmURIHelper(specifier) {
return HELPERS.has(specifier);
}
var readFile = fs.promises.readFile;
function renderRealmURIHelperModule(specifier) {
return readFile(HELPERS.get(specifier), 'utf8');
}
function renderModule(specifier) {
const { context, path, target, transform } = parseRealmURI(specifier);
const lines = [];
if (transform !== TRANSFORM_NONE) {
lines.push(renderTransformImport(transform));
lines.push(renderAncestor('fn', context, path, target));
if (transform === TRANSFORM_INVERT) {
lines.push(`export default transform(fn);\n`);
} else {
lines.push(renderAncestor('lhs', context, path.slice(0, -1)));
lines.push(`export default transform(fn, lhs);\n`);
}
} else if (path.length === 0) {
return readFile(CONTEXTS.get(context), 'utf8');
} else {
const keyDescriptor = path.pop();
let keyExpression;
let propertyAccess;
lines.push(renderAncestor('lhs', context, path));
switch (keyDescriptor.type) {
case KEY_TYPE_GLOBAL_SYMBOL:
lines.push(`import symbolFor from 'realm:Symbol.for';`);
keyExpression = `symbolFor(${ JSON.stringify(keyDescriptor.value) })`;
propertyAccess = `?.[${ keyExpression }]`;
break;
case KEY_TYPE_STRING:
keyExpression = JSON.stringify(keyDescriptor.value);
propertyAccess = isIdentifier(keyDescriptor.value)
? `?.${ keyDescriptor.value }`
: `?.[${ keyExpression }]`;
break;
case KEY_TYPE_WELL_KNOWN_SYMBOL:
lines.push(`import key from 'realm:Symbol.${ keyDescriptor.value }'`);
keyExpression = 'key';
propertyAccess = '?.[key]';
break;
}
if (target === TARGET_VALUE) {
lines.push(`export default lhs${ propertyAccess };\n`);
} else {
lines.push(renderGetTargetImport(target));
lines.push(`export default get(lhs, ${ keyExpression });\n`);
}
}
return lines.join('\n');
}
function renderAncestor(id, context, path, target=TARGET_VALUE) {
return `import ${ id } from ${ JSON.stringify(renderRealmURI({
context, path, target,
transform: TRANSFORM_NONE
})) };`
}
function renderGetTargetImport(target) {
switch (target) {
case TARGET_DESCRIPTOR:
return `import get from '\0realm:target-descriptor';`
case TARGET_GET:
return `import get from '\0realm:target-get';`
case TARGET_SET:
return `import get from '\0realm:target-set';`
}
}
function renderTransformImport(transform) {
switch (transform) {
case TRANSFORM_BIND:
return `import transform from '\0realm:transform-bind';`
case TRANSFORM_INVERT:
return `import transform from '\0realm:transform-invert';`
}
}
function rollupPluginRealmURI() {
return {
name: 'realm-uri',
resolveId: specifier =>
isRealmURIHelper(specifier) ? specifier :
isRealmURI(specifier) ? canonicalizeRealmURI(specifier) :
null,
load: specifier =>
isRealmURIHelper(specifier) ? renderRealmURIHelperModule(specifier) :
isRealmURI(specifier) ? renderModule(specifier) :
null
};
}
export default rollupPluginRealmURI;
//# sourceMappingURL=rollup-plugin-realm-uri.mjs.map