Skip to content

Commit d1304bd

Browse files
committed
Add JavaScript option
1 parent 37c93d2 commit d1304bd

16 files changed

+291
-47
lines changed

res/templates/javaScript.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
document.documentElement.classList.replace('client-nojs', 'client-js');
3+
RLCONF = __ARTICLE_CONFIGVARS_LIST__;
4+
RLSTATE = __ARTICLE_CSS_STATE__;
5+
RLPAGEMODULES = __ARTICLE_JS_LIST__;
6+
</script>
7+
<script>(RLQ=window.RLQ||[]).push(function(){mw.loader.impl(function(){return["user.options@12s5i",function($,jQuery,require,module){mw.user.tokens.set({"patrolToken":"+\\","watchToken":"+\\","csrfToken":"+\\"});}];});});</script>

res/templates/pageFallback.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
<head>
44
<meta charset="UTF-8" />
55
<title></title>
6+
__ARTICLE_JAVASCRIPT__
67
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
78
<link rel="icon" type="image/png" href="__RELATIVE_FILE_PATH____RES_DIR__/favicon.png" />
89
__ARTICLE_CANONICAL_LINK__ __ARTICLE_CSS_BEFORE_META__
10+
<script async src="__RELATIVE_FILE_PATH____MW_DIR__/startup.js"></script>
911
<meta name="ResourceLoaderDynamicStyles" content="" />
1012
__ARTICLE_CSS_AFTER_META__
1113
<link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____MW_DIR__/site.styles.css" />
12-
<link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____MW_DIR__/noscript.css" />
14+
<noscript><link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____MW_DIR__/noscript.css" /></noscript>
1315
<link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____RES_DIR__/footer.css" />
1416
</head>
1517

@@ -24,6 +26,5 @@ <h1 id="firstHeading" class="firstHeading mw-first-heading" __ARTICLE_FIRST_HEAD
2426
<div id="mw-content-text" class="mw-body-content"></div>
2527
</div>
2628
</div>
27-
__ARTICLE_CONFIGVARS_LIST__ __ARTICLE_JS_LIST__
2829
</body>
2930
</html>

res/templates/pageVector2022.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
<head>
44
<meta charset="UTF-8" />
55
<title></title>
6+
__ARTICLE_JAVASCRIPT__
67
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
78
<link rel="icon" type="image/png" href="__RELATIVE_FILE_PATH____RES_DIR__/favicon.png" />
89
__ARTICLE_CANONICAL_LINK__ __ARTICLE_CSS_BEFORE_META__
10+
<script async src="__RELATIVE_FILE_PATH____MW_DIR__/startup.js"></script>
911
<meta name="ResourceLoaderDynamicStyles" content="" />
1012
__ARTICLE_CSS_AFTER_META__
1113
<link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____MW_DIR__/site.styles.css" />
12-
<link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____MW_DIR__/noscript.css" />
14+
<noscript><link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____MW_DIR__/noscript.css" /></noscript>
1315
<link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____RES_DIR__/footer.css" />
1416
<link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____RES_DIR__/vector-2022.css" />
1517
</head>
@@ -32,6 +34,5 @@ <h1 id="firstHeading" class="firstHeading mw-first-heading" __ARTICLE_FIRST_HEAD
3234
</div>
3335
</div>
3436
</div>
35-
__ARTICLE_CONFIGVARS_LIST__ __ARTICLE_JS_LIST__
3637
</body>
3738
</html>

res/templates/pageVectorLegacy.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
<head>
44
<meta charset="UTF-8" />
55
<title></title>
6+
__ARTICLE_JAVASCRIPT__
67
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
78
<link rel="icon" type="image/png" href="__RELATIVE_FILE_PATH____RES_DIR__/favicon.png" />
89
__ARTICLE_CANONICAL_LINK__ __ARTICLE_CSS_BEFORE_META__
10+
<script async src="__RELATIVE_FILE_PATH____MW_DIR__/startup.js"></script>
911
<meta name="ResourceLoaderDynamicStyles" content="" />
1012
__ARTICLE_CSS_AFTER_META__
1113
<link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____MW_DIR__/site.styles.css" />
12-
<link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____MW_DIR__/noscript.css" />
14+
<noscript><link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____MW_DIR__/noscript.css" /></noscript>
1315
<link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____RES_DIR__/footer.css" />
1416
<link rel="stylesheet" type="text/css" href="__RELATIVE_FILE_PATH____RES_DIR__/vector.css" />
1517
</head>
@@ -25,6 +27,5 @@ <h1 id="firstHeading" class="firstHeading mw-first-heading" __ARTICLE_FIRST_HEAD
2527
<div id="mw-content-text" class="mw-body-content"></div>
2628
</div>
2729
</div>
28-
__ARTICLE_CONFIGVARS_LIST__ __ARTICLE_JS_LIST__
2930
</body>
3031
</html>

src/Downloader.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ interface DownloaderOpts {
5959
optimisationCacheUrl: string
6060
s3?: S3
6161
webp: boolean
62+
trustedJs?: string[]
6263
backoffOptions?: BackoffOptions
6364
mwWikiPath?: string
6465
insecure?: boolean
@@ -124,6 +125,7 @@ class Downloader {
124125
private _arrayBufferRequestOptions: AxiosRequestConfig
125126
private _jsonRequestOptions: AxiosRequestConfig
126127
private _streamRequestOptions: AxiosRequestConfig
128+
public trustedJs: string[] = []
127129
public wikimediaMobileJsDependenciesList: string[] = []
128130
public wikimediaMobileStyleDependenciesList: string[] = []
129131

@@ -166,14 +168,15 @@ class Downloader {
166168
return this._apiUrlDirector
167169
}
168170

169-
set init({ uaString, speed, reqTimeout, optimisationCacheUrl, s3, webp, backoffOptions, insecure }: DownloaderOpts) {
171+
set init({ uaString, speed, reqTimeout, optimisationCacheUrl, s3, webp, trustedJs = config.output.mw.js_trusted.slice(), backoffOptions, insecure }: DownloaderOpts) {
170172
this.reset()
171173
this.uaString = uaString
172174
this._speed = speed
173175
this.maxActiveRequests = speed * 10
174176
this._requestTimeout = reqTimeout
175177
this.optimisationCacheUrl = optimisationCacheUrl
176178
this._webp = webp
179+
this.trustedJs = trustedJs
177180
this.s3 = s3
178181
this._apiUrlDirector = new ApiURLDirector(MediaWiki.actionApiUrl.href)
179182
this.insecure = insecure
@@ -271,6 +274,7 @@ class Downloader {
271274
this._requestTimeout = undefined
272275
this.optimisationCacheUrl = undefined
273276
this._webp = false
277+
this.trustedJs = []
274278
this.s3 = undefined
275279
this._apiUrlDirector = undefined
276280
this.insecure = false

src/Gadgets.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class Gadgets {
4343
const module = gadget.metadata.module
4444
if (module.peers && module.peers.length) {
4545
// Only JS Gadgets can have peers
46-
cssGadgets.concat(module.peers)
46+
cssGadgets.push(...module.peers)
4747
return jsGadgets.push(gadget.id)
4848
}
4949
if (module.scripts && module.scripts.length) return jsGadgets.push(gadget.id)

src/Templates.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ const htmlRedirectTemplateCode = () => {
4646
return readTemplate(config.output.templates.htmlRedirect)
4747
}
4848

49+
const javaScriptTemplateCode = () => {
50+
return readTemplate(config.output.templates.javaScript)
51+
}
52+
4953
const articleListHomeTemplate = readTemplate(config.output.templates.articleListHomeTemplate)
5054

5155
export {
@@ -59,6 +63,7 @@ export {
5963
htmlVector2022TemplateCode,
6064
htmlFallbackTemplateCode,
6165
htmlRedirectTemplateCode,
66+
javaScriptTemplateCode,
6267
articleListHomeTemplate,
6368
categoriesTemplate,
6469
subCategoriesTemplate,

src/config.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,38 @@ const config = {
104104
],
105105
js_simplified: [
106106
// base JS scripts always needed / never returned on API calls
107-
'startup',
107+
'jquery',
108+
'mediawiki.base',
109+
],
110+
js_trusted: [
111+
// JS modules and their dependencies trusted to not request any external resources
112+
'jquery',
113+
'mediawiki.base',
114+
'jquery.tablesorter',
115+
'jquery.makeCollapsible',
116+
'mediawiki.page.ready',
117+
'skins.minerva.scripts',
118+
'mediawiki.page.gallery',
119+
'ext.cite.ux-enhancements',
120+
'ext.pygments.view',
121+
'ext.Tabber',
122+
'ext.tabberNeue',
123+
'ext.tmh.player',
124+
'ext.cargo.main',
108125
],
126+
js_dynamic_dependencies: {
127+
'mediawiki.page.ready': [
128+
'jquery.tablesorter',
129+
'jquery.makeCollapsible',
130+
],
131+
'ext.tmh.player': [
132+
'ext.tmh.player.inline',
133+
'ext.tmh.player.dialog',
134+
],
135+
'ext.cargo.main': [
136+
'oojs-ui-core',
137+
],
138+
}
109139
},
110140

111141
// Output paths for storing stuff
@@ -135,6 +165,8 @@ const config = {
135165

136166
subPages: './templates/subpages.html',
137167

168+
javaScript: './templates/javaScript.html',
169+
138170
articleListHomeTemplate: './templates/article_list_home.html',
139171

140172
/* License footer template code */

src/mwoffliner.lib.ts

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import {
2323
MAX_CPU_CORES,
2424
MIN_IMAGE_THRESHOLD_ARTICLELIST_PAGE,
2525
downloadAndSaveModule,
26+
downloadAndSaveStartupModule,
27+
getModuleDependencies,
2628
genCanonicalLink,
2729
genHeaderCSSLink,
2830
genHeaderScript,
@@ -96,6 +98,8 @@ async function execute(argv: any) {
9698
publisher: _publisher,
9799
outputDirectory: _outputDirectory,
98100
addNamespaces: _addNamespaces,
101+
javaScript: _javaScript,
102+
addModules: _addModules,
99103
customZimFavicon,
100104
optimisationCacheUrl,
101105
customFlavour,
@@ -167,6 +171,10 @@ async function execute(argv: any) {
167171
MediaWiki.password = mwPassword
168172
MediaWiki.username = mwUsername
169173

174+
const javaScript = _javaScript || 'trusted'
175+
const addModules = _addModules ? String(_addModules).split(',') : []
176+
const trustedJs = javaScript === 'none' ? null : javaScript === 'trusted' ? config.output.mw.js_trusted.concat(addModules) : []
177+
170178
/* Download helpers; TODO: Merge with something else / expand this. */
171179
Downloader.init = {
172180
uaString: `${config.userAgent} (${adminEmail})`,
@@ -175,6 +183,7 @@ async function execute(argv: any) {
175183
optimisationCacheUrl,
176184
s3,
177185
webp,
186+
trustedJs,
178187
insecure: argv.insecure,
179188
}
180189

@@ -444,12 +453,48 @@ async function execute(argv: any) {
444453
const { jsModuleDependencies, cssModuleDependencies, staticFilesList } = await saveArticles(zimCreator, dump)
445454
logger.log(`Fetching Articles finished in ${(Date.now() - stime) / 1000} seconds`)
446455

447-
logger.log(`Found [${jsModuleDependencies.size}] js module dependencies`)
448-
logger.log(`Found [${cssModuleDependencies.size}] style module dependencies`)
449-
450456
logger.info('Copying Static Resource Files')
451457
await saveStaticFiles(staticFilesList, zimCreator)
452458

459+
if (javaScript === 'none') {
460+
jsModuleDependencies.clear()
461+
} else {
462+
// Get list of all possible modules from startup
463+
const allModules = await downloadAndSaveStartupModule(zimCreator)
464+
addModules.forEach((oneModule) => {
465+
jsModuleDependencies.add(oneModule)
466+
})
467+
// Include known dynamic dependencies
468+
const dynamicJsDeps = config.output.mw.js_dynamic_dependencies
469+
Object.keys(dynamicJsDeps).forEach((oneDep: keyof typeof dynamicJsDeps) => {
470+
if (jsModuleDependencies.has(oneDep)) {
471+
dynamicJsDeps[oneDep].forEach((extraDep) => {
472+
jsModuleDependencies.add(extraDep)
473+
})
474+
}
475+
})
476+
// Include all dependencies of the dependencies
477+
jsModuleDependencies.forEach((oneDep) => {
478+
const oneModule = allModules.find((oneModule) => oneModule[0] === oneDep)
479+
if (!oneModule) {
480+
jsModuleDependencies.delete(oneDep)
481+
return logger.warn(`Unknown JS module [${oneDep}] removed`)
482+
}
483+
getModuleDependencies(oneModule, allModules).forEach((extraDep) => {
484+
jsModuleDependencies.add(extraDep)
485+
})
486+
})
487+
// Don't store JS for CSS modules
488+
cssModuleDependencies.forEach((oneModule) => {
489+
if (!addModules.includes(oneModule)) {
490+
jsModuleDependencies.delete(oneModule)
491+
}
492+
})
493+
}
494+
495+
logger.log(`Found [${jsModuleDependencies.size}] js module dependencies`)
496+
logger.log(`Found [${cssModuleDependencies.size}] style module dependencies`)
497+
453498
const allDependenciesWithType = [
454499
{ type: 'js', moduleList: Array.from(jsModuleDependencies) },
455500
{ type: 'css', moduleList: Array.from(cssModuleDependencies) },
@@ -461,6 +506,9 @@ async function execute(argv: any) {
461506
return pmap(
462507
moduleList,
463508
(oneModule) => {
509+
if (oneModule.startsWith('user')) {
510+
return
511+
}
464512
return downloadAndSaveModule(zimCreator, oneModule, type as any)
465513
},
466514
{ concurrency: Downloader.speed },

src/parameterList.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export const parameterDescriptions = {
3636
withoutZimFullTextIndex: "Don't include a fulltext search index to the ZIM",
3737
webp: 'Convert all jpeg, png and gif images to webp format',
3838
addNamespaces: 'Force additional namespace (comma separated numbers)',
39+
javaScript: 'Amount of JavaScript being allowed in pages, one of the following values can be given: "none", "trusted" or "all" (default being "trusted").',
40+
addModules: 'Add additional ResourceLoader modules for dynamic loading (comma separated list)',
3941
osTmpDir: 'Override default operating system temporary directory path environment variable',
4042
optimisationCacheUrl: 'Object Storage URL (including credentials and bucket name) to cache optimised media files',
4143
forceRender:

0 commit comments

Comments
 (0)