diff --git a/.gitignore b/.gitignore index a834127..516d89a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -dist - # Installed npm modules package-lock.json node_modules diff --git a/dist/ecmarkup.css b/dist/ecmarkup.css new file mode 100644 index 0000000..de9bbfb --- /dev/null +++ b/dist/ecmarkup.css @@ -0,0 +1,945 @@ +body { + display: flex; + font-size: 18px; + line-height: 1.5; + font-family: Cambria, Palatino Linotype, Palatino, Liberation Serif, serif; + padding: 0; + margin: 0; + color: #111; +} + +#spec-container { + padding: 0 20px; + flex-grow: 1; + flex-basis: 66%; + box-sizing: border-box; + overflow: hidden; +} + +body.oldtoc { + margin: 0 auto; +} + +a { + text-decoration: none; + color: #206ca7; +} + +a:visited { + color: #206ca7; +} + +a:hover { + text-decoration: underline; + color: #239dee; +} + + +code { + font-weight: bold; + font-family: Consolas, Monaco, monospace; + white-space: pre; +} + +pre code { + font-weight: inherit; +} + +pre code.hljs { + background-color: #fff; + margin: 0; + padding: 0; +} + +ol.toc { + list-style: none; + padding-left: 0; +} + +ol.toc ol.toc { + padding-left: 2ex; + list-style: none; +} + +var { + color: #2aa198; + transition: background-color 0.25s ease; + cursor: pointer; +} + +var.referenced { + background-color: #ffff33; +} + +emu-const { + font-family: sans-serif; +} + +emu-val { + font-weight: bold; +} + +/* depth 1 */ +emu-alg ol, +/* depth 4 */ +emu-alg ol ol ol ol, +emu-alg ol.nested-thrice, +emu-alg ol.nested-twice ol, +emu-alg ol.nested-once ol ol { + list-style-type: decimal; +} + +/* depth 2 */ +emu-alg ol ol, +emu-alg ol.nested-once, +/* depth 5 */ +emu-alg ol ol ol ol ol, +emu-alg ol.nested-four-times, +emu-alg ol.nested-thrice ol, +emu-alg ol.nested-twice ol ol, +emu-alg ol.nested-once ol ol ol { + list-style-type: lower-alpha; +} + +/* depth 3 */ +emu-alg ol ol ol, +emu-alg ol.nested-twice, +emu-alg ol.nested-once ol, +/* depth 6 */ +emu-alg ol ol ol ol ol ol, +emu-alg ol.nested-lots, +emu-alg ol.nested-four-times ol, +emu-alg ol.nested-thrice ol ol, +emu-alg ol.nested-twice ol ol ol, +emu-alg ol.nested-once ol ol ol ol, +/* depth 7+ */ +emu-alg ol.nested-lots ol { + list-style-type: lower-roman; +} + +emu-eqn { + display: block; + margin-left: 4em; +} + +emu-eqn.inline { + display: inline; + margin: 0; +} + +emu-eqn div:first-child { + margin-left: -2em; +} + +emu-note { + margin: 1em 0; + color: #666; + border-left: 5px solid #ccc; + display: flex; + flex-direction: row; +} + +emu-note > span.note { + flex-basis: 100px; + min-width: 100px; + flex-grow: 0; + flex-shrink: 1; + text-transform: uppercase; + padding-left: 5px; +} + +emu-note[type=editor] { + border-left-color: #faa; +} + +emu-note > div.note-contents { + flex-grow: 1; + flex-shrink: 1; +} + +emu-note > div.note-contents > p:first-of-type { + margin-top: 0; +} + +emu-note > div.note-contents > p:last-of-type { + margin-bottom: 0; +} + +emu-table td code { + white-space: normal; +} + +emu-figure { + display: block; +} + +emu-example { + display: block; + margin: 1em 3em; +} + +emu-example figure figcaption { + margin-top: 0.5em; + text-align: left; +} + +emu-figure figure, +emu-example figure, +emu-table figure { + display: flex; + flex-direction: column; + align-items: center; +} + +emu-production { + display: block; +} + +emu-grammar[type="example"] emu-production, +emu-grammar[type="definition"] emu-production { + margin-top: 1em; + margin-bottom: 1em; + margin-left: 5ex; +} + +emu-grammar.inline, emu-production.inline, +emu-grammar.inline emu-production emu-rhs, emu-production.inline emu-rhs, +emu-grammar[collapsed] emu-production emu-rhs, emu-production[collapsed] emu-rhs { + display: inline; + padding-left: 1ex; + margin-left: 0; +} + +emu-grammar[collapsed] emu-production, emu-production[collapsed] { + margin: 0; +} + +emu-constraints { + font-size: .75em; + margin-right: 1ex; +} + +emu-gann { + margin-right: 1ex; +} + +emu-gann emu-t:last-child, +emu-gann emu-gprose:last-child, +emu-gann emu-nt:last-child { + margin-right: 0; +} + +emu-geq { + margin-left: 1ex; + font-weight: bold; +} + +emu-oneof { + font-weight: bold; + margin-left: 1ex; +} + +emu-nt { + display: inline-block; + font-style: italic; + white-space: nowrap; + text-indent: 0; +} + +emu-nt a, emu-nt a:visited { + color: #333; +} + +emu-rhs emu-nt { + margin-right: 1ex; +} + +emu-t { + display: inline-block; + font-family: monospace; + font-weight: bold; + white-space: nowrap; + text-indent: 0; +} + +emu-production emu-t { + margin-right: 1ex; +} + +emu-rhs { + display: block; + padding-left: 75px; + text-indent: -25px; +} + +emu-mods { + font-size: .85em; + vertical-align: sub; + font-style: normal; + font-weight: normal; +} + +emu-params, emu-opt { + margin-right: 1ex; + font-family: monospace; +} + +emu-params, emu-constraints { + color: #2aa198; +} + +emu-opt { + color: #b58900; +} + +emu-gprose { + font-size: 0.9em; + font-family: Helvetica, Arial, sans-serif; +} + +emu-production emu-gprose { + margin-right: 1ex; +} + +h1.shortname { + color: #f60; + font-size: 1.5em; + margin: 0; +} + +h1.version { + color: #f60; + font-size: 1.5em; + margin: 0; +} + +h1.title { + margin-top: 0; + color: #f60; +} + +h1.first { + margin-top: 0; +} + +h1, h2, h3, h4, h5, h6 { + position: relative; +} + +h1 .secnum { + text-decoration: none; + margin-right: 5px; +} + +h1 span.title { + order: 2; +} + + +h1 { font-size: 2.67em; margin-top: 2em; margin-bottom: 0; line-height: 1em;} +h2 { font-size: 2em; } +h3 { font-size: 1.56em; } +h4 { font-size: 1.25em; } +h5 { font-size: 1.11em; } +h6 { font-size: 1em; } + +h1:hover span.utils { + display: block; +} + +span.utils { + font-size: 18px; + line-height: 18px; + display: none; + position: absolute; + top: 100%; + left: 0; + right: 0; + font-weight: normal; +} + +span.utils:before { + content: "⤷"; + display: inline-block; + padding: 0 5px; +} + +span.utils > * { + display: inline-block; + margin-right: 20px; +} + +h1 span.utils span.anchor a, +h2 span.utils span.anchor a, +h3 span.utils span.anchor a, +h4 span.utils span.anchor a, +h5 span.utils span.anchor a, +h6 span.utils span.anchor a { + text-decoration: none; + font-variant: small-caps; +} + +h1 span.utils span.anchor a:hover, +h2 span.utils span.anchor a:hover, +h3 span.utils span.anchor a:hover, +h4 span.utils span.anchor a:hover, +h5 span.utils span.anchor a:hover, +h6 span.utils span.anchor a:hover { + color: #333; +} + +emu-intro h1, emu-clause h1, emu-annex h1 { font-size: 2em; } +emu-intro h2, emu-clause h2, emu-annex h2 { font-size: 1.56em; } +emu-intro h3, emu-clause h3, emu-annex h3 { font-size: 1.25em; } +emu-intro h4, emu-clause h4, emu-annex h4 { font-size: 1.11em; } +emu-intro h5, emu-clause h5, emu-annex h5 { font-size: 1em; } +emu-intro h6, emu-clause h6, emu-annex h6 { font-size: 0.9em; } +emu-intro emu-intro h1, emu-clause emu-clause h1, emu-annex emu-annex h1 { font-size: 1.56em; } +emu-intro emu-intro h2, emu-clause emu-clause h2, emu-annex emu-annex h2 { font-size: 1.25em; } +emu-intro emu-intro h3, emu-clause emu-clause h3, emu-annex emu-annex h3 { font-size: 1.11em; } +emu-intro emu-intro h4, emu-clause emu-clause h4, emu-annex emu-annex h4 { font-size: 1em; } +emu-intro emu-intro h5, emu-clause emu-clause h5, emu-annex emu-annex h5 { font-size: 0.9em; } +emu-intro emu-intro emu-intro h1, emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex h1 { font-size: 1.25em; } +emu-intro emu-intro emu-intro h2, emu-clause emu-clause emu-clause h2, emu-annex emu-annex emu-annex h2 { font-size: 1.11em; } +emu-intro emu-intro emu-intro h3, emu-clause emu-clause emu-clause h3, emu-annex emu-annex emu-annex h3 { font-size: 1em; } +emu-intro emu-intro emu-intro h4, emu-clause emu-clause emu-clause h4, emu-annex emu-annex emu-annex h4 { font-size: 0.9em; } +emu-intro emu-intro emu-intro emu-intro h1, emu-clause emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex emu-annex h1 { font-size: 1.11em; } +emu-intro emu-intro emu-intro emu-intro h2, emu-clause emu-clause emu-clause emu-clause h2, emu-annex emu-annex emu-annex emu-annex h2 { font-size: 1em; } +emu-intro emu-intro emu-intro emu-intro h3, emu-clause emu-clause emu-clause emu-clause h3, emu-annex emu-annex emu-annex emu-annex h3 { font-size: 0.9em; } +emu-intro emu-intro emu-intro emu-intro emu-intro h1, emu-clause emu-clause emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex emu-annex emu-annex h1 { font-size: 1em; } +emu-intro emu-intro emu-intro emu-intro emu-intro h2, emu-clause emu-clause emu-clause emu-clause emu-clause h2, emu-annex emu-annex emu-annex emu-annex emu-annex h2 { font-size: 0.9em; } +emu-intro emu-intro emu-intro emu-intro emu-intro emu-intro h1, emu-clause emu-clause emu-clause emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex emu-annex emu-annex emu-annex h1 { font-size: 0.9em } + +emu-clause, emu-intro, emu-annex { + display: block; +} + +/* Figures and tables */ +figure { display: block; margin: 1em 0 3em 0; } +figure object { display: block; margin: 0 auto; } +figure table.real-table { margin: 0 auto; } +figure figcaption { + display: block; + color: #555555; + font-weight: bold; + text-align: center; +} + +emu-table table { + margin: 0 auto; +} + +emu-table table, table.real-table { + border-collapse: collapse; +} + +emu-table td, emu-table th, table.real-table td, table.real-table th { + border: 1px solid black; + padding: 0.4em; + vertical-align: baseline; +} +emu-table th, emu-table thead td, table.real-table th { + background-color: #eeeeee; +} + +/* Note: the left content edges of table.lightweight-table >tbody >tr >td + and div.display line up. */ +table.lightweight-table { + border-collapse: collapse; + margin: 0 0 0 1.5em; +} +table.lightweight-table td, table.lightweight-table th { + border: none; + padding: 0 0.5em; + vertical-align: baseline; +} + +/* diff styles */ +ins { + background-color: #e0f8e0; + text-decoration: none; + border-bottom: 1px solid #396; +} + +del { + background-color: #fee; +} + +ins.block, del.block, +emu-production > ins, emu-production > del, +emu-grammar > ins, emu-grammar > del { + display: block; +} +emu-rhs > ins, emu-rhs > del { + display: inline; +} + +tr.ins > td > ins { + border-bottom: none; +} + +tr.ins > td { + background-color: #e0f8e0; +} + +tr.del > td { + background-color: #fee; +} + +/* Menu Styles */ +#menu-toggle { + font-size: 2em; + + position: fixed; + top: 0; + left: 0; + width: 1.5em; + height: 1.5em; + z-index: 3; + visibility: hidden; + color: #1567a2; + background-color: #fff; + + line-height: 1.5em; + text-align: center; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none;; + + cursor: pointer; +} + +#menu { + display: flex; + flex-direction: column; + width: 33%; height: 100vh; + max-width: 500px; + box-sizing: border-box; + background-color: #ddd; + overflow: hidden; + transition: opacity 0.1s linear; + padding: 0 5px; + position: fixed; + left: 0; top: 0; + border-right: 2px solid #bbb; + + z-index: 2; +} + +#menu-spacer { + flex-basis: 33%; + max-width: 500px; + flex-grow: 0; + flex-shrink: 0; +} + +#menu a { + color: #1567a2; +} + +#menu.active { + display: flex; + opacity: 1; + z-index: 2; +} + +#menu-pins { + flex-grow: 1; + display: none; +} + +#menu-pins.active { + display: block; +} + +#menu-pins-list { + margin: 0; + padding: 0; + counter-reset: pins-counter; +} + +#menu-pins-list > li:before { + content: counter(pins-counter); + counter-increment: pins-counter; + display: inline-block; + width: 25px; + text-align: center; + border: 1px solid #bbb; + padding: 2px; + margin: 4px; + box-sizing: border-box; + line-height: 1em; + background-color: #ccc; + border-radius: 4px; +} +#menu-toc > ol { + padding: 0; + flex-grow: 1; +} + +#menu-toc > ol li { + padding: 0; +} + +#menu-toc > ol , #menu-toc > ol ol { + list-style-type: none; + margin: 0; + padding: 0; +} + +#menu-toc > ol ol { + padding-left: 0.75em; +} + +#menu-toc li { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +#menu-toc .item-toggle { + display: inline-block; + transform: rotate(-45deg) translate(-5px, -5px); + transition: transform 0.1s ease; + text-align: center; + width: 20px; + + color: #aab; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none;; + + cursor: pointer; +} + +#menu-toc .item-toggle-none { + display: inline-block; + width: 20px; +} + +#menu-toc li.active > .item-toggle { + transform: rotate(45deg) translate(-5px, -5px); +} + +#menu-toc li > ol { + display: none; +} + +#menu-toc li.active > ol { + display: block; +} + +#menu-toc li.revealed > a { + background-color: #bbb; + font-weight: bold; + /* + background-color: #222; + color: #c6d8e4; + */ +} + +#menu-toc li.revealed-leaf> a { + color: #206ca7; + /* + background-color: #222; + color: #c6d8e4; + */ +} + +#menu-toc li.revealed > .item-toggle { + transform: rotate(45deg) translate(-5px, -5px); +} + +#menu-toc li.revealed > ol { + display: block; +} + +#menu-toc li > a { + padding: 2px 5px; +} + +#menu > * { + margin-bottom: 5px; +} + +.menu-pane-header { + padding: 0 5px; + text-transform: uppercase; + background-color: #aaa; + color: #335; + font-weight: bold; + letter-spacing: 2px; + flex-grow: 0; + flex-shrink: 0; + font-size: 0.8em; +} + +#menu-toc { + display: flex; + flex-direction: column; + width: 100%; + overflow: hidden; + flex-grow: 1; +} + +#menu-toc ol.toc { + overflow-x: hidden; + overflow-y: auto; +} + +#menu-search { + position: relative; + flex-grow: 0; + flex-shrink: 0; + width: 100%; + + display: flex; + flex-direction: column; + + max-height: 300px; +} + +#menu-trace-list { + display: none; +} + +#menu-search-box { + box-sizing: border-box; + display: block; + width: 100%; + margin: 5px 0 0 0; + font-size: 1em; + padding: 2px; + background-color: #bbb; + border: 1px solid #999; +} + +#menu-search-results { + overflow-x: hidden; + overflow-y: auto; +} + +li.menu-search-result-clause:before { + content: 'clause'; + width: 40px; + display: inline-block; + text-align: right; + padding-right: 1ex; + color: #666; + font-size: 75%; +} +li.menu-search-result-op:before { + content: 'op'; + width: 40px; + display: inline-block; + text-align: right; + padding-right: 1ex; + color: #666; + font-size: 75%; +} + +li.menu-search-result-prod:before { + content: 'prod'; + width: 40px; + display: inline-block; + text-align: right; + padding-right: 1ex; + color: #666; + font-size: 75% +} + + +li.menu-search-result-term:before { + content: 'term'; + width: 40px; + display: inline-block; + text-align: right; + padding-right: 1ex; + color: #666; + font-size: 75% +} + +#menu-search-results ul { + padding: 0 5px; + margin: 0; +} + +#menu-search-results li { + white-space: nowrap; + text-overflow: ellipsis; +} + + +#menu-trace-list { + counter-reset: item; + margin: 0 0 0 20px; + padding: 0; +} +#menu-trace-list li { + display: block; + white-space: nowrap; +} + +#menu-trace-list li .secnum:after { + content: " "; +} +#menu-trace-list li:before { + content: counter(item) " "; + background-color: #222; + counter-increment: item; + color: #999; + width: 20px; + height: 20px; + line-height: 20px; + display: inline-block; + text-align: center; + margin: 2px 4px 2px 0; +} + +@media (max-width: 1000px) { + body { + margin: 0; + display: block; + } + + #menu { + display: none; + padding-top: 3em; + width: 450px; + } + + #menu.active { + position: fixed; + height: 100%; + left: 0; + top: 0; + right: 300px; + } + + #menu-toggle { + visibility: visible; + } + + #spec-container { + padding: 0 5px; + } + + #references-pane-spacer { + display: none; + } +} + +@media only screen and (max-width: 800px) { + #menu { + width: 100%; + } + + h1 .secnum:empty { + margin: 0; padding: 0; + } +} + + +/* Toolbox */ +.toolbox { + position: absolute; + background: #ddd; + border: 1px solid #aaa; + display: none; + color: #eee; + padding: 5px; + border-radius: 3px; +} + +.toolbox.active { + display: inline-block; +} + +.toolbox a { + text-decoration: none; + padding: 0 5px; +} + +.toolbox a:hover { + text-decoration: underline; +} + +.toolbox:after, .toolbox:before { + top: 100%; + left: 15px; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} + +.toolbox:after { + border-color: rgba(0, 0, 0, 0); + border-top-color: #ddd; + border-width: 10px; + margin-left: -10px; +} +.toolbox:before { + border-color: rgba(204, 204, 204, 0); + border-top-color: #aaa; + border-width: 12px; + margin-left: -12px; +} + +#references-pane-container { + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: 250px; + display: none; + background-color: #ddd; + z-index: 1; +} + +#references-pane-table-container { + overflow-x: hidden; + overflow-y: auto; +} + +#references-pane-spacer { + flex-basis: 33%; + max-width: 500px; +} + +#references-pane { + flex-grow: 1; + overflow: hidden; + display: flex; + flex-direction: column; +} + +#references-pane-container.active { + display: flex; +} + +#references-pane-close:after { + content: '✖'; + float: right; + cursor: pointer; +} + +#references-pane table tr td:first-child { + text-align: right; + padding-right: 5px; +} + +@media print { + #menu-toggle { + display: none; + } +} diff --git a/dist/ecmarkup.js b/dist/ecmarkup.js new file mode 100644 index 0000000..ef29b3f --- /dev/null +++ b/dist/ecmarkup.js @@ -0,0 +1,897 @@ +"use strict"; + +function Search(menu) { + this.menu = menu; + this.$search = document.getElementById('menu-search'); + this.$searchBox = document.getElementById('menu-search-box'); + this.$searchResults = document.getElementById('menu-search-results'); + + this.loadBiblio(); + + document.addEventListener('keydown', this.documentKeydown.bind(this)); + + this.$searchBox.addEventListener('keydown', debounce(this.searchBoxKeydown.bind(this), { stopPropagation: true })); + this.$searchBox.addEventListener('keyup', debounce(this.searchBoxKeyup.bind(this), { stopPropagation: true })); + + // Perform an initial search if the box is not empty. + if (this.$searchBox.value) { + this.search(this.$searchBox.value); + } +} + +Search.prototype.loadBiblio = function () { + var $biblio = document.getElementById('menu-search-biblio'); + if (!$biblio) { + this.biblio = []; + } else { + this.biblio = JSON.parse($biblio.textContent); + this.biblio.clauses = this.biblio.filter(function (e) { return e.type === 'clause' }); + this.biblio.byId = this.biblio.reduce(function (map, entry) { + map[entry.id] = entry; + return map; + }, {}); + } +} + +Search.prototype.documentKeydown = function (e) { + if (e.keyCode === 191) { + e.preventDefault(); + e.stopPropagation(); + this.triggerSearch(); + } +} + +Search.prototype.searchBoxKeydown = function (e) { + e.stopPropagation(); + e.preventDefault(); + if (e.keyCode === 191 && e.target.value.length === 0) { + e.preventDefault(); + } else if (e.keyCode === 13) { + e.preventDefault(); + this.selectResult(); + } +} + +Search.prototype.searchBoxKeyup = function (e) { + if (e.keyCode === 13 || e.keyCode === 9) { + return; + } + + this.search(e.target.value); +} + + +Search.prototype.triggerSearch = function (e) { + if (this.menu.isVisible()) { + this._closeAfterSearch = false; + } else { + this._closeAfterSearch = true; + this.menu.show(); + } + + this.$searchBox.focus(); + this.$searchBox.select(); +} +// bit 12 - Set if the result starts with searchString +// bits 8-11: 8 - number of chunks multiplied by 2 if cases match, otherwise 1. +// bits 1-7: 127 - length of the entry +// General scheme: prefer case sensitive matches with fewer chunks, and otherwise +// prefer shorter matches. +function relevance(result, searchString) { + var relevance = 0; + + relevance = Math.max(0, 8 - result.match.chunks) << 7; + + if (result.match.caseMatch) { + relevance *= 2; + } + + if (result.match.prefix) { + relevance += 2048 + } + + relevance += Math.max(0, 255 - result.entry.key.length); + + return relevance; +} + +Search.prototype.search = function (searchString) { + if (searchString === '') { + this.displayResults([]); + this.hideSearch(); + return; + } else { + this.showSearch(); + } + + if (searchString.length === 1) { + this.displayResults([]); + return; + } + + var results; + + if (/^[\d\.]*$/.test(searchString)) { + results = this.biblio.clauses.filter(function (clause) { + return clause.number.substring(0, searchString.length) === searchString; + }).map(function (clause) { + return { entry: clause }; + }); + } else { + results = []; + + for (var i = 0; i < this.biblio.length; i++) { + var entry = this.biblio[i]; + if (!entry.key) { + // biblio entries without a key aren't searchable + continue; + } + + var match = fuzzysearch(searchString, entry.key); + if (match) { + results.push({ entry: entry, match: match }); + } + } + + results.forEach(function (result) { + result.relevance = relevance(result, searchString); + }); + + results = results.sort(function (a, b) { return b.relevance - a.relevance }); + + } + + if (results.length > 50) { + results = results.slice(0, 50); + } + + this.displayResults(results); +} +Search.prototype.hideSearch = function () { + this.$search.classList.remove('active'); +} + +Search.prototype.showSearch = function () { + this.$search.classList.add('active'); +} + +Search.prototype.selectResult = function () { + var $first = this.$searchResults.querySelector('li:first-child a'); + + if ($first) { + document.location = $first.getAttribute('href'); + } + + this.$searchBox.value = ''; + this.$searchBox.blur(); + this.displayResults([]); + this.hideSearch(); + + if (this._closeAfterSearch) { + this.menu.hide(); + } +} + +Search.prototype.displayResults = function (results) { + if (results.length > 0) { + this.$searchResults.classList.remove('no-results'); + + var html = '' + + this.$searchResults.innerHTML = html; + } else { + this.$searchResults.innerHTML = ''; + this.$searchResults.classList.add('no-results'); + } +} + + +function Menu() { + this.$toggle = document.getElementById('menu-toggle'); + this.$menu = document.getElementById('menu'); + this.$toc = document.querySelector('menu-toc > ol'); + this.$pins = document.querySelector('#menu-pins'); + this.$pinList = document.getElementById('menu-pins-list'); + this.$toc = document.querySelector('#menu-toc > ol'); + this.$specContainer = document.getElementById('spec-container'); + this.search = new Search(this); + + this._pinnedIds = {}; + this.loadPinEntries(); + + // toggle menu + this.$toggle.addEventListener('click', this.toggle.bind(this)); + + // keydown events for pinned clauses + document.addEventListener('keydown', this.documentKeydown.bind(this)); + + // toc expansion + var tocItems = this.$menu.querySelectorAll('#menu-toc li'); + for (var i = 0; i < tocItems.length; i++) { + var $item = tocItems[i]; + $item.addEventListener('click', function($item, event) { + $item.classList.toggle('active'); + event.stopPropagation(); + }.bind(null, $item)); + } + + // close toc on toc item selection + var tocLinks = this.$menu.querySelectorAll('#menu-toc li > a'); + for (var i = 0; i < tocLinks.length; i++) { + var $link = tocLinks[i]; + $link.addEventListener('click', function(event) { + this.toggle(); + event.stopPropagation(); + }.bind(this)); + } + + // update active clause on scroll + window.addEventListener('scroll', debounce(this.updateActiveClause.bind(this))); + this.updateActiveClause(); + + // prevent menu scrolling from scrolling the body + this.$toc.addEventListener('wheel', function (e) { + var target = e.currentTarget; + var offTop = e.deltaY < 0 && target.scrollTop === 0; + if (offTop) { + e.preventDefault(); + } + var offBottom = e.deltaY > 0 + && target.offsetHeight + target.scrollTop >= target.scrollHeight; + + if (offBottom) { + e.preventDefault(); + } + }); +} + +Menu.prototype.documentKeydown = function (e) { + e.stopPropagation(); + if (e.keyCode === 80) { + this.togglePinEntry(); + } else if (e.keyCode > 48 && e.keyCode < 58) { + this.selectPin(e.keyCode - 49); + } +} + +Menu.prototype.updateActiveClause = function () { + this.setActiveClause(findActiveClause(this.$specContainer)) +} + +Menu.prototype.setActiveClause = function (clause) { + this.$activeClause = clause; + this.revealInToc(this.$activeClause); +} + +Menu.prototype.revealInToc = function (path) { + var current = this.$toc.querySelectorAll('li.revealed'); + for (var i = 0; i < current.length; i++) { + current[i].classList.remove('revealed'); + current[i].classList.remove('revealed-leaf'); + } + + var current = this.$toc; + var index = 0; + while (index < path.length) { + var children = current.children; + for (var i = 0; i < children.length; i++) { + if ('#' + path[index].id === children[i].children[1].getAttribute('href') ) { + children[i].classList.add('revealed'); + if (index === path.length - 1) { + children[i].classList.add('revealed-leaf'); + var rect = children[i].getBoundingClientRect(); + // this.$toc.getBoundingClientRect().top; + var tocRect = this.$toc.getBoundingClientRect(); + if (rect.top + 10 > tocRect.bottom) { + this.$toc.scrollTop = this.$toc.scrollTop + (rect.top - tocRect.bottom) + (rect.bottom - rect.top); + } else if (rect.top < tocRect.top) { + this.$toc.scrollTop = this.$toc.scrollTop - (tocRect.top - rect.top); + } + } + current = children[i].querySelector('ol'); + index++; + break; + } + } + + } +} + +function findActiveClause(root, path) { + var clauses = new ClauseWalker(root); + var $clause; + var path = path || []; + + while ($clause = clauses.nextNode()) { + var rect = $clause.getBoundingClientRect(); + var $header = $clause.querySelector("h1"); + var marginTop = parseInt(getComputedStyle($header)["margin-top"]); + + if ((rect.top - marginTop) <= 0 && rect.bottom > 0) { + return findActiveClause($clause, path.concat($clause)) || path; + } + } + + return path; +} + +function ClauseWalker(root) { + var previous; + var treeWalker = document.createTreeWalker( + root, + NodeFilter.SHOW_ELEMENT, + { + acceptNode: function (node) { + if (previous === node.parentNode) { + return NodeFilter.FILTER_REJECT; + } else { + previous = node; + } + if (node.nodeName === 'EMU-CLAUSE' || node.nodeName === 'EMU-INTRO' || node.nodeName === 'EMU-ANNEX') { + return NodeFilter.FILTER_ACCEPT; + } else { + return NodeFilter.FILTER_SKIP; + } + } + }, + false + ); + + return treeWalker; +} + +Menu.prototype.toggle = function () { + this.$menu.classList.toggle('active'); +} + +Menu.prototype.show = function () { + this.$menu.classList.add('active'); +} + +Menu.prototype.hide = function () { + this.$menu.classList.remove('active'); +} + +Menu.prototype.isVisible = function() { + return this.$menu.classList.contains('active'); +} + +Menu.prototype.showPins = function () { + this.$pins.classList.add('active'); +} + +Menu.prototype.hidePins = function () { + this.$pins.classList.remove('active'); +} + +Menu.prototype.addPinEntry = function (id) { + var entry = this.search.biblio.byId[id]; + if (!entry) { + // id was deleted after pin (or something) so remove it + delete this._pinnedIds[id]; + this.persistPinEntries(); + return; + } + + if (entry.type === 'clause') { + var prefix; + if (entry.number) { + prefix = entry.number + ' '; + } else { + prefix = ''; + } + this.$pinList.innerHTML += '
  • ' + prefix + entry.titleHTML + '
  • '; + } else { + this.$pinList.innerHTML += '
  • ' + entry.key + '
  • '; + } + + if (Object.keys(this._pinnedIds).length === 0) { + this.showPins(); + } + this._pinnedIds[id] = true; + this.persistPinEntries(); +} + +Menu.prototype.removePinEntry = function (id) { + var item = this.$pinList.querySelector('a[href="#' + id + '"]').parentNode; + this.$pinList.removeChild(item); + delete this._pinnedIds[id]; + if (Object.keys(this._pinnedIds).length === 0) { + this.hidePins(); + } + + this.persistPinEntries(); +} + +Menu.prototype.persistPinEntries = function () { + try { + if (!window.localStorage) return; + } catch (e) { + return; + } + + localStorage.pinEntries = JSON.stringify(Object.keys(this._pinnedIds)); +} + +Menu.prototype.loadPinEntries = function () { + try { + if (!window.localStorage) return; + } catch (e) { + return; + } + + var pinsString = window.localStorage.pinEntries; + if (!pinsString) return; + var pins = JSON.parse(pinsString); + for(var i = 0; i < pins.length; i++) { + this.addPinEntry(pins[i]); + } +} + +Menu.prototype.togglePinEntry = function (id) { + if (!id) { + id = this.$activeClause[this.$activeClause.length - 1].id; + } + + if (this._pinnedIds[id]) { + this.removePinEntry(id); + } else { + this.addPinEntry(id); + } +} + +Menu.prototype.selectPin = function (num) { + document.location = this.$pinList.children[num].children[0].href; +} + +var menu; +function init() { + menu = new Menu(); + var $container = document.getElementById('spec-container'); + $container.addEventListener('mouseover', debounce(function (e) { + Toolbox.activateIfMouseOver(e); + })); + document.addEventListener('keydown', debounce(function (e) { + if (e.code === "Escape" && Toolbox.active) { + Toolbox.deactivate(); + } + })); +} + +document.addEventListener('DOMContentLoaded', init); + +function debounce(fn, opts) { + opts = opts || {}; + var timeout; + return function(e) { + if (opts.stopPropagation) { + e.stopPropagation(); + } + var args = arguments; + if (timeout) { + clearTimeout(timeout); + } + timeout = setTimeout(function() { + timeout = null; + fn.apply(this, args); + }.bind(this), 150); + } +} + +var CLAUSE_NODES = ['EMU-CLAUSE', 'EMU-INTRO', 'EMU-ANNEX']; +function findLocalReferences ($elem) { + var name = $elem.innerHTML; + var references = []; + + var parentClause = $elem.parentNode; + while (parentClause && CLAUSE_NODES.indexOf(parentClause.nodeName) === -1) { + parentClause = parentClause.parentNode; + } + + if(!parentClause) return; + + var vars = parentClause.querySelectorAll('var'); + + for (var i = 0; i < vars.length; i++) { + var $var = vars[i]; + + if ($var.innerHTML === name) { + references.push($var); + } + } + + return references; +} + +function toggleFindLocalReferences($elem) { + var references = findLocalReferences($elem); + if ($elem.classList.contains('referenced')) { + references.forEach(function ($reference) { + $reference.classList.remove('referenced'); + }); + } else { + references.forEach(function ($reference) { + $reference.classList.add('referenced'); + }); + } +} + +function installFindLocalReferences () { + document.addEventListener('click', function (e) { + if (e.target.nodeName === 'VAR') { + toggleFindLocalReferences(e.target); + } + }); +} + +document.addEventListener('DOMContentLoaded', installFindLocalReferences); + + + + +// The following license applies to the fuzzysearch function +// The MIT License (MIT) +// Copyright © 2015 Nicolas Bevacqua +// Copyright © 2016 Brian Terlson +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +function fuzzysearch (searchString, haystack, caseInsensitive) { + var tlen = haystack.length; + var qlen = searchString.length; + var chunks = 1; + var finding = false; + + if (qlen > tlen) { + return false; + } + + if (qlen === tlen) { + if (searchString === haystack) { + return { caseMatch: true, chunks: 1, prefix: true }; + } else if (searchString.toLowerCase() === haystack.toLowerCase()) { + return { caseMatch: false, chunks: 1, prefix: true }; + } else { + return false; + } + } + + outer: for (var i = 0, j = 0; i < qlen; i++) { + var nch = searchString[i]; + while (j < tlen) { + var targetChar = haystack[j++]; + if (targetChar === nch) { + finding = true; + continue outer; + } + if (finding) { + chunks++; + finding = false; + } + } + + if (caseInsensitive) { return false; } + + return fuzzysearch(searchString.toLowerCase(), haystack.toLowerCase(), true); + } + + return { caseMatch: !caseInsensitive, chunks: chunks, prefix: j <= qlen }; +} + +var Toolbox = { + init: function () { + this.$container = document.createElement('div'); + this.$container.classList.add('toolbox'); + this.$permalink = document.createElement('a'); + this.$permalink.textContent = 'Permalink'; + this.$pinLink = document.createElement('a'); + this.$pinLink.textContent = 'Pin'; + this.$pinLink.setAttribute('href', '#'); + this.$pinLink.addEventListener('click', function (e) { + e.preventDefault(); + e.stopPropagation(); + menu.togglePinEntry(this.entry.id); + }.bind(this)); + + this.$refsLink = document.createElement('a'); + this.$refsLink.setAttribute('href', '#'); + this.$refsLink.addEventListener('click', function (e) { + e.preventDefault(); + e.stopPropagation(); + referencePane.showReferencesFor(this.entry); + }.bind(this)); + this.$container.appendChild(this.$permalink); + this.$container.appendChild(this.$pinLink); + this.$container.appendChild(this.$refsLink); + document.body.appendChild(this.$container); + }, + + activate: function (el, entry, target) { + if (el === this._activeEl) return; + this.active = true; + this.entry = entry; + this.$container.classList.add('active'); + this.top = el.offsetTop - this.$container.offsetHeight - 10; + this.left = el.offsetLeft; + this.$container.setAttribute('style', 'left: ' + this.left + 'px; top: ' + this.top + 'px'); + this.updatePermalink(); + this.updateReferences(); + this._activeEl = el; + if (this.top < document.body.scrollTop && el === target) { + // don't scroll unless it's a small thing (< 200px) + this.$container.scrollIntoView(); + } + }, + + updatePermalink: function () { + this.$permalink.setAttribute('href', '#' + this.entry.id); + }, + + updateReferences: function () { + this.$refsLink.textContent = 'References (' + this.entry.referencingIds.length + ')'; + }, + + activateIfMouseOver: function (e) { + var ref = this.findReferenceUnder(e.target); + if (ref && (!this.active || e.pageY > this._activeEl.offsetTop)) { + var entry = menu.search.biblio.byId[ref.id]; + this.activate(ref.element, entry, e.target); + } else if (this.active && ((e.pageY < this.top) || e.pageY > (this._activeEl.offsetTop + this._activeEl.offsetHeight))) { + this.deactivate(); + } + }, + + findReferenceUnder: function (el) { + while (el) { + var parent = el.parentNode; + if (el.nodeName === 'H1' && parent.nodeName.match(/EMU-CLAUSE|EMU-ANNEX|EMU-INTRO/) && parent.id) { + return { element: el, id: parent.id }; + } else if (el.nodeName.match(/EMU-(?!CLAUSE|XREF|ANNEX|INTRO)|DFN/) && + el.id && el.id[0] !== '_') { + if (el.nodeName === 'EMU-FIGURE' || el.nodeName === 'EMU-TABLE' || el.nodeName === 'EMU-EXAMPLE') { + // return the figcaption element + return { element: el.children[0].children[0], id: el.id }; + } else if (el.nodeName === 'EMU-PRODUCTION') { + // return the LHS non-terminal element + return { element: el.children[0], id: el.id }; + } else { + return { element: el, id: el.id }; + } + } + el = parent; + } + }, + + deactivate: function () { + this.$container.classList.remove('active'); + this._activeEl = null; + this.activeElBounds = null; + this.active = false; + } +} + +var referencePane = { + init: function() { + this.$container = document.createElement('div'); + this.$container.setAttribute('id', 'references-pane-container'); + + var $spacer = document.createElement('div'); + $spacer.setAttribute('id', 'references-pane-spacer'); + + this.$pane = document.createElement('div'); + this.$pane.setAttribute('id', 'references-pane'); + + this.$container.appendChild($spacer); + this.$container.appendChild(this.$pane); + + this.$header = document.createElement('div'); + this.$header.classList.add('menu-pane-header'); + this.$header.textContent = 'References to '; + this.$headerRefId = document.createElement('a'); + this.$header.appendChild(this.$headerRefId); + this.$closeButton = document.createElement('span'); + this.$closeButton.setAttribute('id', 'references-pane-close'); + this.$closeButton.addEventListener('click', function (e) { + this.deactivate(); + }.bind(this)); + this.$header.appendChild(this.$closeButton); + + this.$pane.appendChild(this.$header); + var tableContainer = document.createElement('div'); + tableContainer.setAttribute('id', 'references-pane-table-container'); + + this.$table = document.createElement('table'); + this.$table.setAttribute('id', 'references-pane-table'); + + this.$tableBody = this.$table.createTBody(); + + tableContainer.appendChild(this.$table); + this.$pane.appendChild(tableContainer); + + menu.$specContainer.appendChild(this.$container); + }, + + activate: function () { + this.$container.classList.add('active'); + }, + + deactivate: function () { + this.$container.classList.remove('active'); + }, + + showReferencesFor(entry) { + this.activate(); + var newBody = document.createElement('tbody'); + var previousId; + var previousCell; + var dupCount = 0; + this.$headerRefId.textContent = '#' + entry.id; + this.$headerRefId.setAttribute('href', '#' + entry.id); + entry.referencingIds.map(function (id) { + var target = document.getElementById(id); + var cid = findParentClauseId(target); + var clause = menu.search.biblio.byId[cid]; + return { id: id, clause: clause } + }).sort(function (a, b) { + return sortByClauseNumber(a.clause, b.clause); + }).forEach(function (record, i) { + if (previousId === record.clause.id) { + previousCell.innerHTML += ' (' + (dupCount + 2) + ')'; + dupCount++; + } else { + var row = newBody.insertRow(); + var cell = row.insertCell(); + cell.innerHTML = record.clause.number; + cell = row.insertCell(); + cell.innerHTML = '' + record.clause.titleHTML + ''; + previousCell = cell; + previousId = record.clause.id; + dupCount = 0; + } + }, this); + this.$table.removeChild(this.$tableBody); + this.$tableBody = newBody; + this.$table.appendChild(this.$tableBody); + } +} +function findParentClauseId(node) { + while (node && node.nodeName !== 'EMU-CLAUSE' && node.nodeName !== 'EMU-INTRO' && node.nodeName !== 'EMU-ANNEX') { + node = node.parentNode; + } + if (!node) return null; + return node.getAttribute('id'); +} + +function sortByClauseNumber(c1, c2) { + var c1c = c1.number.split('.'); + var c2c = c2.number.split('.'); + + for (var i = 0; i < c1c.length; i++) { + if (i >= c2c.length) { + return 1; + } + + var c1 = c1c[i]; + var c2 = c2c[i]; + var c1cn = Number(c1); + var c2cn = Number(c2); + + if (Number.isNaN(c1cn) && Number.isNaN(c2cn)) { + if (c1 > c2) { + return 1; + } else if (c1 < c2) { + return -1; + } + } else if (!Number.isNaN(c1cn) && Number.isNaN(c2cn)) { + return -1; + } else if (Number.isNaN(c1cn) && !Number.isNaN(c2cn)) { + return 1; + } else if(c1cn > c2cn) { + return 1; + } else if (c1cn < c2cn) { + return -1; + } + } + + if (c1c.length === c2c.length) { + return 0; + } + return -1; +} + +document.addEventListener('DOMContentLoaded', function () { + Toolbox.init(); + referencePane.init(); +}) +var CLAUSE_NODES = ['EMU-CLAUSE', 'EMU-INTRO', 'EMU-ANNEX']; +function findLocalReferences ($elem) { + var name = $elem.innerHTML; + var references = []; + + var parentClause = $elem.parentNode; + while (parentClause && CLAUSE_NODES.indexOf(parentClause.nodeName) === -1) { + parentClause = parentClause.parentNode; + } + + if(!parentClause) return; + + var vars = parentClause.querySelectorAll('var'); + + for (var i = 0; i < vars.length; i++) { + var $var = vars[i]; + + if ($var.innerHTML === name) { + references.push($var); + } + } + + return references; +} + +function toggleFindLocalReferences($elem) { + var references = findLocalReferences($elem); + if ($elem.classList.contains('referenced')) { + references.forEach(function ($reference) { + $reference.classList.remove('referenced'); + }); + } else { + references.forEach(function ($reference) { + $reference.classList.add('referenced'); + }); + } +} + +function installFindLocalReferences () { + document.addEventListener('click', function (e) { + if (e.target.nodeName === 'VAR') { + toggleFindLocalReferences(e.target); + } + }); +} + +document.addEventListener('DOMContentLoaded', installFindLocalReferences); diff --git a/dist/index.html b/dist/index.html new file mode 100644 index 0000000..9b214a1 --- /dev/null +++ b/dist/index.html @@ -0,0 +1,384 @@ + + + + +Import Attributes +

    Stage 3 Draft / January 22, 2023

    + +

    Import Attributes

    +

    Import Attributes are an extension to the import syntax that allows specifying additional information to affect how the module is imported. This proposal, other than defining such syntax, also defines the first import attribtue: type, which is passed to the host to specify the expected type of the loaded module. See the explainer for more information.

    +

    + Possible future extensions include: +

    +

    +

    Not every import attribute needs to interact with host semantics, for example module and defer could be defined completely within ECMA-262.

    +
    + + +

    1 Syntax

    + + + ImportDeclaration:importImportClauseFromClause; + importModuleSpecifier; + importImportClauseFromClause[no LineTerminator here]AttributesClause; + importModuleSpecifier[no LineTerminator here]AttributesClause; + + + ExportDeclaration:exportExportFromClauseFromClause; + exportExportFromClauseFromClause[no LineTerminator here]AttributesClause; + exportNamedExports; + exportVariableStatement[~Yield, ~Await] + exportDeclaration[~Yield, ~Await] + exportdefaultHoistableDeclaration[~Yield, ~Await, +Default] + exportdefaultClassDeclaration[~Yield, ~Await, +Default] + exportdefault[lookahead ∉ { function, async + [no LineTerminator here] + function, class }]AssignmentExpression[+In, ~Yield, ~Await]; + + + AttributesClause:with{} + with{AttributeEntries,opt} + + + AttributeEntries:AttributeEntry + AttributeEntries,AttributeEntry + + + AttributeEntry:AttributeKey:StringLiteral + + + + AttributeKey:IdentifierName + StringLiteral + + + ImportCall[Yield, Await]:import(AssignmentExpression[+In, ?Yield, ?Await],opt) + import(AssignmentExpression[+In, ?Yield, ?Await],AssignmentExpression[+In, ?Yield, ?Await],opt) + +
    + + +

    2 Semantics

    + + Editor's Note

    Many productions operating on grammar are the same whether or not an AttributesClause/second ImportCall parameter is included; the new parameter is ignored. In this section, only the semantically significant changes are included, and the PR to merge into the main specification would fill in the straightforward details.

    + + +

    2.1 Import Calls

    + + +

    2.1.1 Runtime Semantics: Evaluation

    + + +

    2.1.1.1 EvaluateImportCall ( specifierExpression [ , optionsExpression ] )

    +
    1. Let referencingScriptOrModule be ! GetActiveScriptOrModule().
    2. Let specifierRef be the result of evaluating specifierExpression.
    3. Let specifier be ? GetValue(specifierRef).
    4. If optionsExpression is present, then
      1. Let optionsRef be the result of evaluating optionsExpression.
      2. Let options be ? GetValue(optionsRef).
    5. Else,
      1. Let options be undefined.
    6. Let promiseCapability be ! NewPromiseCapability(%Promise%).
    7. Let specifierString be ToString(specifier).
    8. IfAbruptRejectPromise(specifierString, promiseCapability).
    9. Let moduleRequest be a new ModuleRequest Record { [[Specifier]]: specifierString, [[Type]]: empty }.
    10. If options is not undefined, then
      1. If Type(options) is not Object,
        1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
        2. Return promiseCapability.[[Promise]].
      2. Let attributesObj be Get(options, "with").
      3. IfAbruptRejectPromise(attributesObj, promiseCapability).
      4. If attributesObj is not undefined,
        1. If Type(attributesObj) is not Object,
          1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
          2. Return promiseCapability.[[Promise]].
        2. Let keys be EnumerableOwnPropertyNames(attributesObj, key).
        3. IfAbruptRejectPromise(keys, promiseCapability).
        4. For each String key of keys,
          1. Let value be Get(attributesObj, key).
          2. IfAbruptRejectPromise(value, promiseCapability).
          3. If Type(value) is not String, then
            1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
            2. Return promiseCapability.[[Promise]].
          4. If key is "type", then
            1. Assert: moduleRequest.[[Type]] is empty.
            2. Set moduleRequest.[[Type]] to value.
          5. TODO: Throw on unknown keys?
    11. Perform HostLoadImportedModule(referrer, moduleRequest, empty, promiseCapability).
    12. Return promiseCapability.[[Promise]].
    +
    + + + ImportCall:import(AssignmentExpression,opt) + +
    1. Return ? EvaluateImportCall(AssignmentExpression).
    + + + ImportCall:import(AssignmentExpression,AssignmentExpression,opt) + +
    1. Return ? EvaluateImportCall(the first AssignmentExpression, the second AssignmentExpression).
    +
    +
    + + +

    2.2 HostLoadImportedModule ( referrer, specifiermoduleRequest, hostDefined, payload )

    +

    The host-defined abstract operation HostLoadImportedModule takes arguments referrer (a Script Record, a Cyclic Module Record, or a Realm Record), specifier (a String)moduleRequest (a ModuleRequest Record), hostDefined (anything), and payload (a GraphLoadingState Record or a PromiseCapability Record) and returns unused.

    + + Note
    +

    An example of when referrer can be a Realm Record is in a web browser host. There, if a user clicks on a control given by

    + +
    <button type="button" onclick="import('./foo.mjs')">Click me</button>
    + +

    there will be no active script or module at the time the import() expression runs. More generally, this can happen in any situation where the host pushes execution contexts with null ScriptOrModule components onto the execution context stack.

    +
    + +

    An implementation of HostLoadImportedModule must conform to the following requirements:

    +
      +
    • + The host environment must perform FinishLoadingImportedModule(referrer, specifiermoduleRequest, payload, result), where result is either a normal completion containing the loaded Module Record or a throw completion, either synchronously or asynchronously. +
    • +
    • +

      If this operation is called multiple times with the same (referrer, specifier) pair(referrer, moduleRequest.[[Specifier]], moduleRequest.[[Type]]) triple and it performs FinishLoadingImportedModule(referrer, specifiermoduleRequest, payload, result) where result is a normal completion, then it must perform FinishLoadingImportedModule(referrer, specifiermoduleRequest, payload, result) with the same result each time.

      +
    • +
    • + The operation must treat payload as an opaque value to be passed through to FinishLoadingImportedModule. +
    • +
    +
    + + +

    2.3 FinishLoadingImportedModule ( referrer, specifiermoduleRequest, payload, result )

    +

    The abstract operation FinishLoadingImportedModule takes arguments referrer (a Script Record, a Cyclic Module Record, or a Realm Record), specifier (a String)moduleRequest (a ModuleRequest Record), payload (a GraphLoadingState Record or a PromiseCapability Record), and result (either a normal completion containing a Module Record or a throw completion) and returns unused.

    + +
    1. If result is a normal completion, then
      1. If referrer.[[LoadedModules]] contains a Record record such that record.[[Specifier]] is moduleRequest.[[Specifier]] and record.[[Type]] is moduleRequest.[[Type]], then
        1. Assert: record.[[Module]] is result.[[Value]].
      2. Else, add Record { [[Specifier]]: moduleRequest.[[Specifier]], [[Type]]: moduleRequest.[[Type]], [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]].
    2. If payload is a GraphLoadingState Record, then
      1. Perform ContinueModuleLoading(payload, result).
    3. Else,
      1. Perform ContinueDynamicImport(payload, result).
    4. Return unused.
    + + Editor's Note
    +

    The description of the [[LoadedModules]] field of Realm Record, Script Record, and Cyclic Module Record should be updated to include the [[Type]] field.

    +
    +
    + + +

    2.4 Static Semantics: CollectImportAttributes ( moduleRequest )

    + + AttributeEntries:AttributeEntries,AttributeEntry + +
    1. Perform CollectImportAttributes of AttributeEntries with parameters « moduleRequest ».
    2. Perform CollectImportAttributes of AttributeEntry with parameters « moduleRequest ».
    + + + AttributeEntry:AttributeKey:StringLiteral + +
    1. Let key be StringValue of AttributeKey.
    2. If key is "type", then
      1. If moduleRequest.[[Type]] is not empty, throw a SyntaxError exception.
      2. Set moduleRequest.[[Type]] to the StringValue of StringLiteral.
    3. TODO: Throw on unknown keys?
    +
    + + +

    2.5 ModuleRequest Records

    + +

    A ModuleRequest Record represents the request to import a module with given import attributes. It consists of the following fields:

    +
    Table 1: ModuleRequest Record fields
    + + + + + + + + + + + + + + + + + + +
    + Field Name + + Value Type + + Meaning +
    + [[Specifier]] + + String + + The module specifier +
    + [[Type]] + + a String, or empty + + The type attribute of this import +
    +
    + + Editor's Note
    In general, this proposal replaces places where module specifiers are passed around with ModuleRequest Records. For example, several syntax-directed operations, such as ModuleRequests produce Lists of ModuleRequest Records rather than Lists of Strings which are interpreted as module specifiers. Some algorithms like ImportEntries and ImportEntriesForModule pass around ModuleRequest Records rather than Strings, in a way which doesn't require any particular textual change. Additionally, record fields in Cyclic Module Records and Source Text Module Records which contained Lists of Strings are replaced by Lists of ModuleRequest Records, as indicated above.
    +
    + +
    Table 2: Additional Fields of Cyclic Module Records
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Field Name + + Value Type + + Meaning +
    + [[Status]] + + unlinked | linking | linked | evaluating | evaluated + + Initially unlinked. Transitions to linking, linked, evaluating, evaluated (in that order) as the module progresses throughout its lifecycle. +
    + [[EvaluationError]] + + An abrupt completion | undefined + + A completion of type throw representing the exception that occurred during evaluation. undefined if no exception occurred or if [[Status]] is not evaluated. +
    + [[DFSIndex]] + + Integer | undefined + + Auxiliary field used during Link and Evaluate only. + If [[Status]] is linking or evaluating, this nonnegative number records the point at which the module was first visited during the ongoing depth-first traversal of the dependency graph. +
    + [[DFSAncestorIndex]] + + Integer | undefined + + Auxiliary field used during Link and Evaluate only. If [[Status]] is linking or evaluating, this is either the module's own [[DFSIndex]] or that of an "earlier" module in the same strongly connected component. +
    + [[RequestedModules]] + + List of StringModuleRequest Record + + A List of all the ModuleSpecifier strings with the corresponding type import attribute used by the module represented by this record to request the importation of a module. The List is source code occurrence ordered. +
    +
    + +

    An ImportEntry Record is a Record that digests information about a single declarative import. Each ImportEntry Record has the fields defined in Table 3:

    +
    Table 3: ImportEntry Record Fields
    + + + + + + + + + + + + + + + + + + + + + + + +
    + Field Name + + Value Type + + Meaning +
    + [[ModuleRequest]] + + String + ModuleRequest Record + + String value of the ModuleSpecifier of the ImportDeclaration. + ModuleRequest Record representing the ModuleSpecifier and the type import attribute of the ImportDeclaration. +
    + [[ImportName]] + + String + + The name under which the desired binding is exported by the module identified by [[ModuleRequest]]. The value "*" indicates that the import request is for the target module's namespace object. +
    + [[LocalName]] + + String + + The name that is used to locally access the imported value from within the importing module. +
    +
    + + +

    2.6 Static Semantics: ModuleRequests

    + + + ImportDeclaration:importImportClauseFromClause; + +
    1. Return ModuleRequests of FromClause.
    2. Let specifier be StringValue of FromClause.
    3. Return a ModuleRequest Record { [[Specifer]]: specifier, [[Type]]: empty }.
    + + ImportDeclaration:importImportClauseFromClauseAttributesClause; + +
    1. Let specifier be StringValue of FromClause.
    2. Let request be the ModuleRequest Record { [[Specifer]]: specifier, [[Type]]: empty }.
    3. Perform CollectImportAttributes of AttributesClause with argument request.
    4. Return request.
    + + Module:[empty] + +
    1. Return a new empty List.
    + + ModuleItemList:ModuleItem + +
    1. Return ModuleRequests of ModuleItem.
    + + ModuleItemList:ModuleItemListModuleItem + +
    1. Let moduleNames be ModuleRequests of ModuleItemList.
    2. Let additionalNames be ModuleRequests of ModuleItem.
    3. Append to moduleNames each element of additionalNames that is not already an element of moduleNames.
    4. Return moduleNames.
    + + ModuleItem:StatementListItem + +
    1. Return a new empty List.
    + + ExportDeclaration:exportExportFromClauseFromClause; + +
    1. Return ModuleRequests of FromClause.
    2. Let specifier be StringValue of FromClause.
    3. Return the ModuleRequest Record { [[Specifer]]: specifier, [[Type]]: empty }.
    + + ExportDeclaration:exportExportFromClauseFromClauseAttributesClause; + +
    1. Let specifier be StringValue of the StringLiteral contained in FromClause.
    2. Let request be the ModuleRequest Record { [[Specifer]]: specifier, [[Type]]: empty }.
    3. Perform CollectImportAttributes of AttributesClause with argument request.
    4. Return request.
    + + ExportDeclaration:exportNamedExports; + exportVariableStatement + exportDeclaration + exportdefaultHoistableDeclaration + exportdefaultClassDeclaration + exportdefaultAssignmentExpression; + +
    1. Return a new empty List.
    +
    +
    + + +

    A Sample host integration: The Web embedding

    + +

    The import attributes proposal is intended to give key information about how modules are interpreted to hosts. For the Web embedding and environments which aim to be similar to it, the string is interpreted as the "module type". This is not the primary way the module type is determined (which, on the Web, would be the MIME type, and in other environments may be the file extension), but rather a secondary check which is required to pass for the module graph to load.

    + +

    In the Web embedding, the following changes would be made to the HTML specification for import attributes:

    + + + +

    The module map is keyed by the absolute URL and the type. Initially no other import attributes are supported, so they are not present.

    +
    +
    \ No newline at end of file diff --git a/spec.html b/spec.html index 28fbff6..580388f 100644 --- a/spec.html +++ b/spec.html @@ -2,9 +2,9 @@ -import assertions +Import Attributes
    -  title: Import assertions
    +  title: Import attributes
       status: proposal
       stage: 3
       location: https://github.com/tc39/proposal-import-assertions
    @@ -12,8 +12,16 @@
       contributors: Sven Sauleau, Myles Borins, Daniel Ehrenberg, Daniel Clark
     
    -

    import assertions

    -

    See the explainer for information.

    +

    Import Attributes

    +

    Import Attributes are an extension to the import syntax that allows specifying additional information to affect how the module is imported. This proposal, other than defining such syntax, also defines the first import attribtue: `type`, which is passed to the host to specify the expected type of the loaded module. See the explainer for more information.

    +

    + Possible future extensions include: +

    +

    +

    Not every import attribute needs to interact with host semantics, for example `module` and `defer` could be defined completely within ECMA-262.

    @@ -23,12 +31,12 @@

    Syntax

    ImportDeclaration : `import` ImportClause FromClause `;` `import` ModuleSpecifier `;` - `import` ImportClause FromClause [no LineTerminator here] AssertClause `;` - `import` ModuleSpecifier [no LineTerminator here] AssertClause `;` + `import` ImportClause FromClause [no LineTerminator here] AttributesClause `;` + `import` ModuleSpecifier [no LineTerminator here] AttributesClause `;` ExportDeclaration : `export` ExportFromClause FromClause `;` - `export` ExportFromClause FromClause [no LineTerminator here] AssertClause `;` + `export` ExportFromClause FromClause [no LineTerminator here] AttributesClause `;` `export` NamedExports `;` `export` VariableStatement[~Yield, ~Await] `export` Declaration[~Yield, ~Await] @@ -36,15 +44,18 @@

    Syntax

    `export` `default` ClassDeclaration[~Yield, ~Await, +Default] `export` `default` [lookahead <! {`function`, `async` [no |LineTerminator| here] `function`, `class`}] AssignmentExpression[+In, ~Yield, ~Await] `;` - AssertClause : - `assert` `{` `}` - `assert` `{` AssertEntries `,`? `}` + AttributesClause : + `with` `{` `}` + `with` `{` AttributeEntries `,`? `}` + + AttributeEntries : + AttributeEntry + AttributeEntries `,` AttributeEntry - AssertEntries : - AssertionKey `:` StringLiteral - AssertionKey `:` StringLiteral `,` AssertEntries + AttributeEntry : + AttributeKey `:` StringLiteral - AssertionKey: + AttributeKey: IdentifierName StringLiteral @@ -57,7 +68,7 @@

    Syntax

    Semantics

    -

    Many productions operating on grammar are the same whether or not an |AssertClause|/second |ImportCall| parameter is included; the new parameter is ignored. In this section, only the semantically significant changes are included, and the PR to merge into the main specification would fill in the straightforward details.

    +

    Many productions operating on grammar are the same whether or not an |AttributesClause|/second |ImportCall| parameter is included; the new parameter is ignored. In this section, only the semantically significant changes are included, and the PR to merge into the main specification would fill in the straightforward details.

    Import Calls

    @@ -79,30 +90,29 @@

    EvaluateImportCall ( _specifierExpression_ [ , _optionsExpression_ ] )

    1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%). 1. Let _specifierString_ be ToString(_specifier_). 1. IfAbruptRejectPromise(_specifierString_, _promiseCapability_). - 1. Let _assertions_ be a new empty List. + 1. Let _moduleRequest_ be a new ModuleRequest Record { [[Specifier]]: _specifierString_, [[Type]]: ~empty~ }. 1. If _options_ is not *undefined*, then 1. If Type(_options_) is not Object, 1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « a newly created *TypeError* object »). 1. Return _promiseCapability_.[[Promise]]. - 1. Let _assertionsObj_ be Get(_options_, *"assert"*). - 1. IfAbruptRejectPromise(_assertionsObj_, _promiseCapability_). - 1. If _assertionsObj_ is not *undefined*, - 1. If Type(_assertionsObj_) is not Object, + 1. Let _attributesObj_ be Get(_options_, *"with"*). + 1. IfAbruptRejectPromise(_attributesObj_, _promiseCapability_). + 1. If _attributesObj_ is not *undefined*, + 1. If Type(_attributesObj_) is not Object, 1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « a newly created *TypeError* object »). 1. Return _promiseCapability_.[[Promise]]. - 1. Let _keys_ be EnumerableOwnPropertyNames(_assertionsObj_, ~key~). + 1. Let _keys_ be EnumerableOwnPropertyNames(_attributesObj_, ~key~). 1. IfAbruptRejectPromise(_keys_, _promiseCapability_). - 1. Let _supportedAssertions_ be ! HostGetSupportedImportAssertions(). 1. For each String _key_ of _keys_, - 1. Let _value_ be Get(_assertionsObj_, _key_). + 1. Let _value_ be Get(_attributesObj_, _key_). 1. IfAbruptRejectPromise(_value_, _promiseCapability_). 1. If Type(_value_) is not String, then 1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « a newly created *TypeError* object »). 1. Return _promiseCapability_.[[Promise]]. - 1. If _supportedAssertions_ contains _key_, then - 1. Append { [[Key]]: _key_, [[Value]]: _value_ } to _assertions_. - 1. Sort _assertions_ by the code point order of the [[Key]] of each element. NOTE: This sorting is observable only in that hosts are prohibited from distinguishing among assertions by the order they occur in. - 1. Let _moduleRequest_ be a new ModuleRequest Record { [[Specifier]]: _specifierString_, [[Assertions]]: _assertions_ }. + 1. If _key_ is *"type"*, then + 1. Assert: _moduleRequest_.[[Type]] is ~empty~. + 1. Set _moduleRequest_.[[Type]] to _value_. + 1. TODO: Throw on unknown keys? 1. Perform HostLoadImportedModule(_referrer_, _moduleRequest_, ~empty~, _promiseCapability_). 1. Return _promiseCapability_.[[Promise]]. @@ -138,35 +148,12 @@

    HostLoadImportedModule ( _referrer_, _specifier__moduleReque The host environment must perform FinishLoadingImportedModule(_referrer_, _specifier__moduleRequest_, _payload_, _result_), where _result_ is either a normal completion containing the loaded Module Record or a throw completion, either synchronously or asynchronously.
  • -

    If this operation is called multiple times with the same (_referrer_, _specifier_) pair(_referrer_, _moduleRequest_.[[Specifier]], _moduleRequest_.[[Assertions]]) triple and it performs FinishLoadingImportedModule(_referrer_, _specifier__moduleRequest_, _payload_, _result_) where _result_ is a normal completion, then it must perform FinishLoadingImportedModule(_referrer_, _specifier__moduleRequest_, _payload_, _result_) with the same _result_ each time.

    - -

    It is recommended but not required that implementations additionally conform to the following stronger constraint:

    -
      -
    • - If this operation is called multiple times with the same (_referrer_, _moduleRequest_.[[Specifier]]) pair and it performs FinishLoadingImportedModule(_referrer_, _moduleRequest_, _payload_, _result_) where _result_ is a normal completion, then it must perform FinishLoadingImportedModule(_referrer_, _moduleRequest_, _payload_, _result_) with the same _result_ each time. -
    • -
    -
  • -
  • - _moduleRequest_.[[Assertions]] must not influence the interpretation of the module or the module specifier; instead, it may be used to determine whether the algorithm completes normally or with an abrupt completion. +

    If this operation is called multiple times with the same (_referrer_, _specifier_) pair(_referrer_, _moduleRequest_.[[Specifier]], _moduleRequest_.[[Type]]) triple and it performs FinishLoadingImportedModule(_referrer_, _specifier__moduleRequest_, _payload_, _result_) where _result_ is a normal completion, then it must perform FinishLoadingImportedModule(_referrer_, _specifier__moduleRequest_, _payload_, _result_) with the same _result_ each time.

  • The operation must treat _payload_ as an opaque value to be passed through to FinishLoadingImportedModule.
  • - -

    The actual process performed is host-defined, but typically consists of performing whatever I/O operations are necessary to load the appropriate Module Record. Multiple different (_referrer_, _specifier__moduleRequest_.[[Specifier]]) pairs may map to the same Module Record instance. The actual mapping semantic is host-defined but typically a normalization process is applied to _specifier__moduleRequest_.[[Specifier]] as part of the mapping process. A typical normalization process would include actions such as expansion of relative and abbreviated path specifiers.

    - - -

    The above text implies that is recommended but not required that hosts do not use _moduleRequest_.[[Assertions]] as part of the module cache key. In either case, an exception thrown from an import with a given assertion list does not rule out success of another import with the same specifier but a different assertion list.

    -

    Assertions do not affect the contents of the module. Future follow-up proposals may relax this restriction with "evaluator attributes" that would change the contents of the module. There are three possible ways to handle multiple imports of the same module with "evaluator attributes":

    -
      -
    • Race and use the attribute that was requested by the first import. This seems broken--the second usage is ignored.
    • -
    • Reject the module graph and don't load if attributes differ. This seems bad for composition--using two unrelated packages together could break, if they load the same module with disagreeing attributes.
    • -
    • Clone and make two copies of the module, for the different ways it's transformed. In this case, the attributes would have to be part of the cache key. These semantics would run counter to the intuition that there is just one copy of a module.
    • -
    -

    It's possible that one of these three options may make sense for a module load, on a case-by-case basis by attribute, but it's worth careful thought before making this choice.

    -
    @@ -175,9 +162,9 @@

    FinishLoadingImportedModule ( _referrer_, _specifier__module 1. If _result_ is a normal completion, then - 1. If _referrer_.[[LoadedModules]] contains a Record _record_ such that _record_.[[Specifier]] is _moduleRequest_.[[Specifier]] and AssertionsEqual(_record_.[[Assertions]], _moduleRequest_.[[Assertions]]) is *true*, then + 1. If _referrer_.[[LoadedModules]] contains a Record _record_ such that _record_.[[Specifier]] is _moduleRequest_.[[Specifier]] and _record_.[[Type]] is _moduleRequest_.[[Type]], then 1. Assert: _record_.[[Module]] is _result_.[[Value]]. - 1. Else, add Record { [[Specifier]]: _moduleRequest_.[[Specifier]], [[Assertions]]: _moduleRequest_.[[Assertions]], [[Module]]: _result_.[[Value]] } to _referrer_.[[LoadedModules]]. + 1. Else, add Record { [[Specifier]]: _moduleRequest_.[[Specifier]], [[Type]]: _moduleRequest_.[[Type]], [[Module]]: _result_.[[Value]] } to _referrer_.[[LoadedModules]]. 1. If _payload_ is a GraphLoadingState Record, then 1. Perform ContinueModuleLoading(_payload_, _result_). 1. Else, @@ -186,109 +173,32 @@

    FinishLoadingImportedModule ( _referrer_, _specifier__module -

    The description of the [[LoadedModules]] field of Realm Record, Script Record, and Cyclic Module Record should be updated to include the [[Assertions]] field.

    +

    The description of the [[LoadedModules]] field of Realm Record, Script Record, and Cyclic Module Record should be updated to include the [[Type]] field.

    - - -

    AssertionsEqual(_left_, _right_)

    -

    The abstract operation AssertionsEqual takes arguments _left_ and _right_ (two Lists of Records { [[Key]]: a String, [[Value]]: a String }), and returns a Boolean.

    - - - 1. If the number of elements in _left_ is not the same as the number of elements in _right_, return *false*. - 1. For each Record { [[Key]], [[Value]] } _r_ of _left_, do - 1. Let _found_ be *false*. - 1. For each Record { [[Key]], [[Value]] } _s_ of _right_, do - 1. If _r_.[[Key]] is _s_.[[Key]] and _r_.[[Value]] is _s_.[[Value]], then - 1. Set _found_ to *true*. - 1. If _found_ is *false*, return *false*. - 1. Return *true*. - -
    - -

    Static Semantics: HostGetSupportedImportAssertions ()

    -

    - HostGetSupportedImportAssertions is a host-defined abstract operation that allows host environments to specify which import assertions they support. - Only assertions with supported keys will be provided to the host. -

    - -

    The implementation of HostGetSupportedImportAssertions must conform to the following requrements:

    - -
      -
    • It must return a List whose values are all StringValues, each indicating a supported assertion.
    • - -
    • Each time this operation is called, it must return the same List instance with the same contents.
    • - -
    • An implementation of HostGetSupportedImportAssertions must always complete normally (i.e., not return an abrupt completion).
    • -
    - -

    The default implementation of HostGetSupportedImportAssertions is to return an empty List.

    - - The purpose of requiring the host to specify its supported import assertions, rather than passing all assertions to the host and letting it then choose which ones it wants to handle, is to ensure that unsupported assertions are handled in a consistent way across different hosts. -
    - - -

    Static Semantics: Early Errors

    - AssertClause : `assert` `{` AssertEntries `,`? `}` -
      -
    • It is a Syntax Error if AssertClauseToAssertions of |AssertClause| has two entries _a_ and _b_ such that _a_.[[Key]] is _b_.[[Key]].
    • -
    -
    - - -

    Static Semantics: AssertClauseToAssertions

    - AssertClause : `assert` `{` `}` - - 1. Return a new empty List. - - - AssertClause : `assert` `{` AssertEntries `,`? `}` - - 1. Let _assertions_ be AssertClauseToAssertions of |AssertEntries|. - 1. Sort _assertions_ by the code point order of the [[Key]] of each element. NOTE: This sorting is observable only in that hosts are prohibited from distinguishing among assertions by the order they occur in. - 1. Return _assertions_. - - - AssertEntries : AssertionKey `:` StringLiteral + +

    Static Semantics: CollectImportAttributes ( _moduleRequest_ )

    + AttributeEntries : AttributeEntries `,` AttributeEntry - 1. Let _supportedAssertions_ be !HostGetSupportedImportAssertions(). - 1. Let _key_ be StringValue of |AssertionKey|. - 1. If _supportedAssertions_ contains _key_, - 1. Let _entry_ be a Record { [[Key]]: _key_, [[Value]]: StringValue of |StringLiteral| }. - 1. Return a new List containing the single element, _entry_. - 1. Otherwise, return a new empty List. + 1. Perform CollectImportAttributes of |AttributeEntries| with parameters « _moduleRequest_ ». + 1. Perform CollectImportAttributes of |AttributeEntry| with parameters « _moduleRequest_ ». - AssertEntries : AssertionKey `:` StringLiteral `,` AssertEntries + AttributeEntry : AttributeKey `:` StringLiteral - 1. Let _supportedAssertions_ be !HostGetSupportedImportAssertions(). - 1. Let _key_ be StringValue of |AssertionKey|. - 1. If _supportedAssertions_ contains _key_, - 1. Let _entry_ be a Record { [[Key]]: _key_, [[Value]]: StringValue of |StringLiteral| }. - 1. Let _rest_ be AssertClauseToAssertions of |AssertEntries|. - 1. Return a new List containing _entry_ followed by the elements of _rest_. - 1. Otherwise, return AssertClauseToAssertions of |AssertEntries|. - -
    - - -

    Static Semantics: StringValue

    - AssertionKey : IdentifierName - - 1. Return the StringValue of |IdentifierName|. - - - AssertionKey : StringLiteral - - 1. Return the StringValue of |StringLiteral|. + 1. Let _key_ be StringValue of |AttributeKey|. + 1. If _key_ is *"type"*, then + 1. If _moduleRequest_.[[Type]] is not ~empty~, throw a SyntaxError exception. + 1. Set _moduleRequest_.[[Type]] to the StringValue of |StringLiteral|. + 1. TODO: Throw on unknown keys?

    ModuleRequest Records

    -

    A ModuleRequest Record represents the request to import a module with given import assertions. It consists of the following fields:

    +

    A ModuleRequest Record represents the request to import a module with given import attributes. It consists of the following fields:

    @@ -316,13 +226,13 @@

    ModuleRequest Records

    @@ -399,7 +309,7 @@

    ModuleRequest Records

    List of StringModuleRequest Record @@ -431,7 +341,7 @@

    ModuleRequest Records

    @@ -466,14 +376,15 @@

    Static Semantics: ModuleRequests

    ImportDeclaration : `import` ImportClause FromClause `;` 1. Return ModuleRequests of |FromClause|. - 1. Let _specifier_ be StringValue of the |StringLiteral| contained in |FromClause|. - 1. Return a ModuleRequest Record { [[Specifer]]: _specifier_, [[Assertions]]: an empty List }. + 1. Let _specifier_ be StringValue of |FromClause|. + 1. Return a ModuleRequest Record { [[Specifer]]: _specifier_, [[Type]]: ~empty~ }. - ImportDeclaration : `import` ImportClause FromClause AssertClause `;` + ImportDeclaration : `import` ImportClause FromClause AttributesClause `;` - 1. Let _specifier_ be StringValue of the |StringLiteral| contained in |FromClause|. - 1. Let _assertions_ be AssertClauseToAssertions of |AssertClause|. - 1. Return a ModuleRequest Record { [[Specifer]]: _specifier_, [[Assertions]]: _assertions_ }. + 1. Let _specifier_ be StringValue of |FromClause|. + 1. Let _request_ be the ModuleRequest Record { [[Specifer]]: _specifier_, [[Type]]: ~empty~ }. + 1. Perform CollectImportAttributes of |AttributesClause| with argument _request_. + 1. Return _request_. Module : [empty] @@ -487,10 +398,9 @@

    Static Semantics: ModuleRequests

    1. Let _moduleNames_ be ModuleRequests of |ModuleItemList|. 1. Let _additionalNames_ be ModuleRequests of |ModuleItem|. - 1. Append to _moduleNames_ each element of _additionalNames_ that is not already an element of _moduleNames_. + 1. Append to _moduleNames_ each element of _additionalNames_ that is not already an element of _moduleNames_. 1. Return _moduleNames_. - Deletion of duplicates is an unnecessary "spec optimization" that would be more complicated to explain in terms of examining import assertion records, and can be simply removed. ModuleItem : StatementListItem 1. Return a new empty List. @@ -500,16 +410,17 @@

    Static Semantics: ModuleRequests

    1. Return ModuleRequests of |FromClause|. - 1. Let _specifier_ be StringValue of the |StringLiteral| contained in |FromClause|. - 1. Return a ModuleRequest Record { [[Specifer]]: _specifier_, [[Assertions]]: an empty List }. + 1. Let _specifier_ be StringValue of |FromClause|. + 1. Return the ModuleRequest Record { [[Specifer]]: _specifier_, [[Type]]: ~empty~ }. - ExportDeclaration : `export` ExportFromClause FromClause AssertClause `;` + ExportDeclaration : `export` ExportFromClause FromClause AttributesClause `;` 1. Let _specifier_ be StringValue of the |StringLiteral| contained in |FromClause|. - 1. Let _assertions_ be AssertClauseToAssertions of |AssertClause|. - 1. Return a ModuleRequest Record { [[Specifer]]: _specifier_, [[Assertions]]: _assertions_ }. + 1. Let _request_ be the ModuleRequest Record { [[Specifer]]: _specifier_, [[Type]]: ~empty~ }. + 1. Perform CollectImportAttributes of |AttributesClause| with argument _request_. + 1. Return _request_. ExportDeclaration : @@ -529,13 +440,13 @@

    Static Semantics: ModuleRequests

    Sample host integration: The Web embedding

    -

    The import assertions proposal is intended to give key information about how modules are interpreted to hosts. For the Web embedding and environments which aim to be similar to it, the string is interpreted as the "module type". This is not the primary way the module type is determined (which, on the Web, would be the MIME type, and in other environments may be the file extension), but rather a secondary check which is required to pass for the module graph to load.

    +

    The import attributes proposal is intended to give key information about how modules are interpreted to hosts. For the Web embedding and environments which aim to be similar to it, the string is interpreted as the "module type". This is not the primary way the module type is determined (which, on the Web, would be the MIME type, and in other environments may be the file extension), but rather a secondary check which is required to pass for the module graph to load.

    -

    In the Web embedding, the following changes would be made to the HTML specification for import assertions:

    +

    In the Web embedding, the following changes would be made to the HTML specification for import attributes:

    • The module script would have an additional item, which would be the module type, as a string (e.g., *"json"*), or *undefined* for a JavaScript module.
    • -
    • HostLoadImportedModule would take a ModuleRequest Record parameter in place of a specifier string, which would be passed down through several abstract operations to reach the fetch a single module script algorithm. Somewhere near the entrypoint, if the ModuleRequest Record's [[Assertions]] field has an element _entry_ such that _entry_.[[Key]] is *"type"*, then let _type_ be _entry_.[[Value]]; otherwise let _type_ be *undefined*. If the type is invalid, then an exception is thrown and module loading fails. Otherwise, this will equal the module type, if the module can be successfully fetched with a matching MIME type.
    • +
    • HostLoadImportedModule would take a ModuleRequest Record parameter in place of a specifier string, which would be passed down through several abstract operations to reach the fetch a single module script algorithm. Somewhere near the entrypoint, if the ModuleRequest Record's [[Type]] field has an element _entry_ such that _entry_.[[Key]] is *"type"*, then let _type_ be _entry_.[[Value]]; otherwise let _type_ be *undefined*. If the type is invalid, then an exception is thrown and module loading fails. Otherwise, this will equal the module type, if the module can be successfully fetched with a matching MIME type.
    • In the fetch the descendents of a module script algorithm, when iterating over [[RequestedModules]], the elements are ModuleRequest Records rather than just specifier strings; these Records is passed on to the internal module script graph fetching procedure (which sends it to "fetch a single module script". Other usage sites of [[RequestedModules]] ignore the assertion.
    • "Fetch a single module script" would check the assertion in two places:
        @@ -545,5 +456,5 @@

        Sample host integration: The Web embedding

      -

      The module map is keyed by the absolute URL and the _type_. Initially no other import assertions are supported, so they are not present.

      +

      The module map is keyed by the absolute URL and the _type_. Initially no other import attributes are supported, so they are not present.

    - [[Assertions]] + [[Type]] - a List of Records { [[Key]]: a String, [[Value]]: a String } + a String, or ~empty~ - The import assertions + The `type` attribute of this import
    - A List of all the |ModuleSpecifier| strings and import assertions used by the module represented by this record to request the importation of a module. The List is source code occurrence ordered. + A List of all the |ModuleSpecifier| strings with the corresponding `type` import attribute used by the module represented by this record to request the importation of a module. The List is source code occurrence ordered.
    String value of the |ModuleSpecifier| of the |ImportDeclaration|. - ModuleRequest Record representing the |ModuleSpecifier| and import assertions of the |ImportDeclaration|. + ModuleRequest Record representing the |ModuleSpecifier| and the `type` import attribute of the |ImportDeclaration|.