Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
be0d5b0c
Commit
be0d5b0c
authored
Sep 01, 2023
by
Robert Knight
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Convert boot script modules to TypeScript
parent
45fe9d7f
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
67 additions
and
97 deletions
+67
-97
rollup-boot.config.mjs
rollup-boot.config.mjs
+1
-1
boot.ts
src/boot/boot.ts
+53
-69
browser-check.ts
src/boot/browser-check.ts
+1
-3
index.ts
src/boot/index.ts
+6
-10
parse-json-config.ts
src/boot/parse-json-config.ts
+3
-4
url-template.ts
src/boot/url-template.ts
+3
-10
No files found.
rollup-boot.config.mjs
View file @
be0d5b0c
...
@@ -36,7 +36,7 @@ const assetRoot = isProd
...
@@ -36,7 +36,7 @@ const assetRoot = isProd
:
`
${
localhost
}
:3001/hypothesis/
${
version
}
/`
;
:
`
${
localhost
}
:3001/hypothesis/
${
version
}
/`
;
export
default
{
export
default
{
input
:
'src/boot/index.
j
s'
,
input
:
'src/boot/index.
t
s'
,
output
:
{
output
:
{
file
:
'build/boot.js'
,
file
:
'build/boot.js'
,
...
...
src/boot/boot.
j
s
→
src/boot/boot.
t
s
View file @
be0d5b0c
/**
export
type
SidebarAppConfig
=
{
* @typedef SidebarAppConfig
/** The root URL to which URLs in `manifest` are relative. */
* @prop {string} assetRoot - The root URL to which URLs in `manifest` are relative
assetRoot
:
string
;
* @prop {Record<string,string>} manifest -
* A mapping from canonical asset path to cache-busted asset path
/** A mapping from canonical asset path to cache-busted asset path. */
* @prop {string} apiUrl
manifest
:
Record
<
string
,
string
>
;
*/
apiUrl
:
string
;
};
/**
* @typedef AnnotatorConfig
export
type
AnnotatorConfig
=
{
* @prop {string} assetRoot - The root URL to which URLs in `manifest` are relative
/** The root URL to which URLs in `manifest` are relative. */
* @prop {string} notebookAppUrl - The URL of the sidebar's notebook
assetRoot
:
string
;
* @prop {string} profileAppUrl - The URL of the sidebar's user profile view
/** The URL of the sidebar's notebook. */
* @prop {string} sidebarAppUrl - The URL of the sidebar's HTML page
notebookAppUrl
:
string
;
* @prop {Record<string,string>} manifest -
/** The URL of the sidebar's user profile view. */
* A mapping from canonical asset path to cache-busted asset path
profileAppUrl
:
string
;
*/
/** The URL of the sidebar's HTML page. */
sidebarAppUrl
:
string
;
/**
/** A mapping from canonical asset path to cache-busted asset path. */
* @typedef {Window & { PDFViewerApplication?: object }} MaybePDFWindow
manifest
:
Record
<
string
,
string
>
;
*/
};
type
MaybePDFWindow
=
Window
&
{
PDFViewerApplication
?:
object
};
/**
/**
* Mark an element as having been added by the boot script.
* Mark an element as having been added by the boot script.
*
*
* This marker is later used to know which elements to remove when unloading
* This marker is later used to know which elements to remove when unloading
* the client.
* the client.
*
* @param {HTMLElement} el
*/
*/
function
tagElement
(
el
)
{
function
tagElement
(
el
:
HTMLElement
)
{
el
.
setAttribute
(
'data-hypothesis-asset'
,
''
);
el
.
setAttribute
(
'data-hypothesis-asset'
,
''
);
}
}
/**
function
injectStylesheet
(
doc
:
Document
,
href
:
string
)
{
* @param {Document} doc
* @param {string} href
*/
function
injectStylesheet
(
doc
,
href
)
{
const
link
=
doc
.
createElement
(
'link'
);
const
link
=
doc
.
createElement
(
'link'
);
link
.
rel
=
'stylesheet'
;
link
.
rel
=
'stylesheet'
;
link
.
type
=
'text/css'
;
link
.
type
=
'text/css'
;
...
@@ -46,14 +42,19 @@ function injectStylesheet(doc, href) {
...
@@ -46,14 +42,19 @@ function injectStylesheet(doc, href) {
doc
.
head
.
appendChild
(
link
);
doc
.
head
.
appendChild
(
link
);
}
}
/**
function
injectScript
(
* @param {Document} doc
doc
:
Document
,
* @param {string} src - The script URL
src
:
string
,
* @param {object} options
{
* @param {boolean} [options.esModule] - Whether to load the script as an ES module
esModule
=
true
,
* @param {boolean} [options.forceReload] - Whether to force re-evaluation of an ES module script
forceReload
=
false
,
*/
}:
{
function
injectScript
(
doc
,
src
,
{
esModule
=
true
,
forceReload
=
false
}
=
{})
{
/** Whether to load the script as an ES module. */
esModule
?:
boolean
;
/** Whether to force re-evaluation of an ES module script. */
forceReload
?:
boolean
;
}
=
{},
)
{
const
script
=
doc
.
createElement
(
'script'
);
const
script
=
doc
.
createElement
(
'script'
);
if
(
esModule
)
{
if
(
esModule
)
{
...
@@ -79,13 +80,12 @@ function injectScript(doc, src, { esModule = true, forceReload = false } = {}) {
...
@@ -79,13 +80,12 @@ function injectScript(doc, src, { esModule = true, forceReload = false } = {}) {
doc
.
head
.
appendChild
(
script
);
doc
.
head
.
appendChild
(
script
);
}
}
/**
function
injectLink
(
* @param {Document} doc
doc
:
Document
,
* @param {string} rel
rel
:
string
,
* @param {'html'|'javascript'} type
type
:
'html'
|
'javascript'
,
* @param {string} url
url
:
string
,
*/
)
{
function
injectLink
(
doc
,
rel
,
type
,
url
)
{
const
link
=
doc
.
createElement
(
'link'
);
const
link
=
doc
.
createElement
(
'link'
);
link
.
rel
=
rel
;
link
.
rel
=
rel
;
link
.
href
=
url
;
link
.
href
=
url
;
...
@@ -100,12 +100,8 @@ function injectLink(doc, rel, type, url) {
...
@@ -100,12 +100,8 @@ function injectLink(doc, rel, type, url) {
*
*
* This can be used to preload an API request or other resource which we know
* This can be used to preload an API request or other resource which we know
* that the client will load.
* that the client will load.
*
* @param {Document} doc
* @param {string} type - Type of resource
* @param {string} url
*/
*/
function
preloadURL
(
doc
,
type
,
url
)
{
function
preloadURL
(
doc
:
Document
,
type
:
string
,
url
:
string
)
{
const
link
=
doc
.
createElement
(
'link'
);
const
link
=
doc
.
createElement
(
'link'
);
link
.
rel
=
'preload'
;
link
.
rel
=
'preload'
;
link
.
as
=
type
;
link
.
as
=
type
;
...
@@ -122,11 +118,7 @@ function preloadURL(doc, type, url) {
...
@@ -122,11 +118,7 @@ function preloadURL(doc, type, url) {
doc
.
head
.
appendChild
(
link
);
doc
.
head
.
appendChild
(
link
);
}
}
/**
function
assetURL
(
config
:
SidebarAppConfig
|
AnnotatorConfig
,
path
:
string
)
{
* @param {SidebarAppConfig|AnnotatorConfig} config
* @param {string} path
*/
function
assetURL
(
config
,
path
)
{
return
config
.
assetRoot
+
'build/'
+
config
.
manifest
[
path
];
return
config
.
assetRoot
+
'build/'
+
config
.
manifest
[
path
];
}
}
...
@@ -136,11 +128,8 @@ function assetURL(config, path) {
...
@@ -136,11 +128,8 @@ function assetURL(config, path) {
* This triggers loading of the necessary resources for the client in a host
* This triggers loading of the necessary resources for the client in a host
* or guest frame. We could in future simplify booting in guest-only frames
* or guest frame. We could in future simplify booting in guest-only frames
* by omitting resources that are only needed in the host frame.
* by omitting resources that are only needed in the host frame.
*
* @param {Document} doc
* @param {AnnotatorConfig} config
*/
*/
export
function
bootHypothesisClient
(
doc
,
c
onfig
)
{
export
function
bootHypothesisClient
(
doc
:
Document
,
config
:
AnnotatorC
onfig
)
{
// Detect presence of Hypothesis in the page
// Detect presence of Hypothesis in the page
const
appLinkEl
=
doc
.
querySelector
(
const
appLinkEl
=
doc
.
querySelector
(
'link[type="application/annotator+html"]'
,
'link[type="application/annotator+html"]'
,
...
@@ -173,19 +162,17 @@ export function bootHypothesisClient(doc, config) {
...
@@ -173,19 +162,17 @@ export function bootHypothesisClient(doc, config) {
);
);
const
scripts
=
[
'scripts/annotator.bundle.js'
];
const
scripts
=
[
'scripts/annotator.bundle.js'
];
for
(
le
t
path
of
scripts
)
{
for
(
cons
t
path
of
scripts
)
{
const
url
=
assetURL
(
config
,
path
);
const
url
=
assetURL
(
config
,
path
);
injectScript
(
doc
,
url
,
{
esModule
:
false
});
injectScript
(
doc
,
url
,
{
esModule
:
false
});
}
}
const
styles
=
[];
const
styles
=
[];
if
(
if
((
window
as
MaybePDFWindow
).
PDFViewerApplication
!==
undefined
)
{
/** @type {MaybePDFWindow} */
(
window
).
PDFViewerApplication
!==
undefined
)
{
styles
.
push
(
'styles/pdfjs-overrides.css'
);
styles
.
push
(
'styles/pdfjs-overrides.css'
);
}
}
styles
.
push
(
'styles/highlights.css'
);
styles
.
push
(
'styles/highlights.css'
);
for
(
le
t
path
of
styles
)
{
for
(
cons
t
path
of
styles
)
{
const
url
=
assetURL
(
config
,
path
);
const
url
=
assetURL
(
config
,
path
);
injectStylesheet
(
doc
,
url
);
injectStylesheet
(
doc
,
url
);
}
}
...
@@ -193,23 +180,20 @@ export function bootHypothesisClient(doc, config) {
...
@@ -193,23 +180,20 @@ export function bootHypothesisClient(doc, config) {
/**
/**
* Bootstrap the sidebar application which displays annotations.
* Bootstrap the sidebar application which displays annotations.
*
* @param {Document} doc
* @param {SidebarAppConfig} config
*/
*/
export
function
bootSidebarApp
(
doc
,
c
onfig
)
{
export
function
bootSidebarApp
(
doc
:
Document
,
config
:
SidebarAppC
onfig
)
{
// Preload `/api/` and `/api/links` API responses.
// Preload `/api/` and `/api/links` API responses.
preloadURL
(
doc
,
'fetch'
,
config
.
apiUrl
);
preloadURL
(
doc
,
'fetch'
,
config
.
apiUrl
);
preloadURL
(
doc
,
'fetch'
,
config
.
apiUrl
+
'links'
);
preloadURL
(
doc
,
'fetch'
,
config
.
apiUrl
+
'links'
);
const
scripts
=
[
'scripts/sidebar.bundle.js'
];
const
scripts
=
[
'scripts/sidebar.bundle.js'
];
for
(
le
t
path
of
scripts
)
{
for
(
cons
t
path
of
scripts
)
{
const
url
=
assetURL
(
config
,
path
);
const
url
=
assetURL
(
config
,
path
);
injectScript
(
doc
,
url
,
{
esModule
:
true
});
injectScript
(
doc
,
url
,
{
esModule
:
true
});
}
}
const
styles
=
[
'styles/katex.min.css'
,
'styles/sidebar.css'
];
const
styles
=
[
'styles/katex.min.css'
,
'styles/sidebar.css'
];
for
(
le
t
path
of
styles
)
{
for
(
cons
t
path
of
styles
)
{
const
url
=
assetURL
(
config
,
path
);
const
url
=
assetURL
(
config
,
path
);
injectStylesheet
(
doc
,
url
);
injectStylesheet
(
doc
,
url
);
}
}
...
...
src/boot/browser-check.
j
s
→
src/boot/browser-check.
t
s
View file @
be0d5b0c
...
@@ -5,10 +5,8 @@
...
@@ -5,10 +5,8 @@
* We use feature tests to try to avoid false negatives, accepting some risk of
* We use feature tests to try to avoid false negatives, accepting some risk of
* false positives due to the host page having loaded polyfills for APIs in order
* false positives due to the host page having loaded polyfills for APIs in order
* to support older browsers.
* to support older browsers.
*
* @return {boolean}
*/
*/
export
function
isBrowserSupported
()
{
export
function
isBrowserSupported
()
:
boolean
{
// Checks that return a truthy value if they succeed and throw or return
// Checks that return a truthy value if they succeed and throw or return
// a falsey value if they fail.
// a falsey value if they fail.
const
checks
=
[
const
checks
=
[
...
...
src/boot/index.
j
s
→
src/boot/index.
t
s
View file @
be0d5b0c
...
@@ -8,26 +8,22 @@
...
@@ -8,26 +8,22 @@
// @ts-ignore - This file is generated before the boot bundle is built.
// @ts-ignore - This file is generated before the boot bundle is built.
import
manifest
from
'../../build/manifest.json'
;
import
manifest
from
'../../build/manifest.json'
;
import
{
bootHypothesisClient
,
bootSidebarApp
}
from
'./boot'
;
import
{
bootHypothesisClient
,
bootSidebarApp
}
from
'./boot'
;
import
type
{
AnnotatorConfig
,
SidebarAppConfig
}
from
'./boot'
;
import
{
isBrowserSupported
}
from
'./browser-check'
;
import
{
isBrowserSupported
}
from
'./browser-check'
;
import
{
getExtensionId
,
hasExtensionConfig
}
from
'./browser-extension-utils'
;
import
{
getExtensionId
,
hasExtensionConfig
}
from
'./browser-extension-utils'
;
import
{
parseJsonConfig
}
from
'./parse-json-config'
;
import
{
parseJsonConfig
}
from
'./parse-json-config'
;
import
{
processUrlTemplate
}
from
'./url-template'
;
import
{
processUrlTemplate
}
from
'./url-template'
;
/**
* @typedef {import('./boot').AnnotatorConfig} AnnotatorConfig
* @typedef {import('./boot').SidebarAppConfig} SidebarAppConfig
*/
if
(
isBrowserSupported
())
{
if
(
isBrowserSupported
())
{
const
config
=
/** @type {AnnotatorConfig|SidebarAppConfig} */
(
const
config
=
parseJsonConfig
(
document
)
as
parseJsonConfig
(
document
)
|
AnnotatorConfig
)
;
|
SidebarAppConfig
;
const
assetRoot
=
processUrlTemplate
(
config
.
assetRoot
||
'__ASSET_ROOT__'
);
const
assetRoot
=
processUrlTemplate
(
config
.
assetRoot
||
'__ASSET_ROOT__'
);
// Check whether this is a mini-app (indicated by the presence of a
// Check whether this is a mini-app (indicated by the presence of a
// `<hypothesis-app>` element) and load the appropriate part of the client.
// `<hypothesis-app>` element) and load the appropriate part of the client.
if
(
document
.
querySelector
(
'hypothesis-app'
))
{
if
(
document
.
querySelector
(
'hypothesis-app'
))
{
const
sidebarConfig
=
/** @type {SidebarAppConfig} */
(
config
)
;
const
sidebarConfig
=
config
as
SidebarAppConfig
;
bootSidebarApp
(
document
,
{
bootSidebarApp
(
document
,
{
assetRoot
,
assetRoot
,
manifest
,
manifest
,
...
@@ -45,7 +41,7 @@ if (isBrowserSupported()) {
...
@@ -45,7 +41,7 @@ if (isBrowserSupported()) {
// nb. If new asset URLs are added here, the browser extension and
// nb. If new asset URLs are added here, the browser extension and
// `hypothesis-injector.ts` need to be updated.
// `hypothesis-injector.ts` need to be updated.
const
annotatorConfig
=
/** @type {AnnotatorConfig} */
(
config
)
;
const
annotatorConfig
=
config
as
AnnotatorConfig
;
const
notebookAppUrl
=
processUrlTemplate
(
const
notebookAppUrl
=
processUrlTemplate
(
annotatorConfig
.
notebookAppUrl
||
'__NOTEBOOK_APP_URL__'
,
annotatorConfig
.
notebookAppUrl
||
'__NOTEBOOK_APP_URL__'
,
);
);
...
...
src/boot/parse-json-config.
j
s
→
src/boot/parse-json-config.
t
s
View file @
be0d5b0c
...
@@ -12,11 +12,10 @@
...
@@ -12,11 +12,10 @@
* setting names, scripts further down in the document override those further
* setting names, scripts further down in the document override those further
* up).
* up).
*
*
* @param
{Document|Element}
document - The root element to search.
* @param document - The root element to search.
*/
*/
export
function
parseJsonConfig
(
document
)
{
export
function
parseJsonConfig
(
document
:
Document
|
Element
)
{
/** @type {Record<string, unknown>} */
const
config
:
Record
<
string
,
unknown
>
=
{};
const
config
=
{};
const
settingsElements
=
document
.
querySelectorAll
(
const
settingsElements
=
document
.
querySelectorAll
(
'script.js-hypothesis-config'
,
'script.js-hypothesis-config'
,
);
);
...
...
src/boot/url-template.
j
s
→
src/boot/url-template.
t
s
View file @
be0d5b0c
...
@@ -4,10 +4,8 @@
...
@@ -4,10 +4,8 @@
* We don't use the URL constructor here because IE and early versions of Edge
* We don't use the URL constructor here because IE and early versions of Edge
* do not support it and this code runs early in the life of the app before any
* do not support it and this code runs early in the life of the app before any
* polyfills can be loaded.
* polyfills can be loaded.
*
* @param {string} url
*/
*/
function
extractOrigin
(
url
)
{
function
extractOrigin
(
url
:
string
)
{
const
match
=
url
.
match
(
/
(
https
?)
:
\/\/([^
:
/]
+
)
/
);
const
match
=
url
.
match
(
/
(
https
?)
:
\/\/([^
:
/]
+
)
/
);
if
(
!
match
)
{
if
(
!
match
)
{
return
null
;
return
null
;
...
@@ -16,9 +14,7 @@ function extractOrigin(url) {
...
@@ -16,9 +14,7 @@ function extractOrigin(url) {
}
}
function
currentScriptOrigin
(
document_
=
document
)
{
function
currentScriptOrigin
(
document_
=
document
)
{
const
scriptEl
=
/** @type {HTMLScriptElement|null} */
(
const
scriptEl
=
document_
.
currentScript
as
HTMLScriptElement
|
null
;
document_
.
currentScript
);
if
(
!
scriptEl
)
{
if
(
!
scriptEl
)
{
// Function was called outside of initial script execution.
// Function was called outside of initial script execution.
return
null
;
return
null
;
...
@@ -34,11 +30,8 @@ function currentScriptOrigin(document_ = document) {
...
@@ -34,11 +30,8 @@ function currentScriptOrigin(document_ = document) {
* from a device or VM that is not the system where the development server is
* from a device or VM that is not the system where the development server is
* running. In that case, all references to `localhost` need to be replaced
* running. In that case, all references to `localhost` need to be replaced
* with the IP/hostname of the dev server.
* with the IP/hostname of the dev server.
*
* @param {string} url
* @param {Document} document_
*/
*/
export
function
processUrlTemplate
(
url
,
document_
=
document
)
{
export
function
processUrlTemplate
(
url
:
string
,
document_
=
document
)
{
if
(
url
.
indexOf
(
'{'
)
===
-
1
)
{
if
(
url
.
indexOf
(
'{'
)
===
-
1
)
{
// Not a template. This should always be the case in production.
// Not a template. This should always be the case in production.
return
url
;
return
url
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment