UNPKG

rollup-plugin-realm-uri

Version:

Import deduplicated references to intrinsic values using realm: scheme URIs

458 lines (382 loc) 11.8 kB
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