UNPKG

4.27 kBJavaScriptView Raw
1const Listr = require('listr')
2const got = require('got')
3const { JSDOM } = require('jsdom')
4const bresolve = require('browser-resolve')
5const Promise = require('bluebird')
6const readFile = require('mz/fs').readFile
7
8const pmPath = bresolve.sync('plug-modules', { filename: __filename })
9
10// stubs needed by plug.dj's app code at boot time
11const stubs = {
12 localStorage: {
13 getItem () {},
14 setItem () {},
15 clear () {}
16 },
17 gapi: {
18 client: {
19 setApiKey () {},
20 load () {}
21 }
22 },
23 Intercom: {},
24 amplitude: { __VERSION__: true },
25 FB: { init: function () {} }
26}
27
28function waitForRequireJs (window) {
29 return new Promise((resolve, reject) => {
30 // wait for the app javascript to load, then run plug-modules
31 const intv = setInterval(waitForRequireJs, 20)
32 function waitForRequireJs () {
33 if (window.requirejs) {
34 window.define('facebook', stubs.FB)
35 // intercept plug.dj's booting code
36 // plug.dj uses require([ deps ]) calls in places to actually start the
37 // app. we do want those calls to register the dependencies with require,
38 // but we don't want to start plug.dj because that's expensive and
39 // usually throws an error somewhere. instead we override the callback
40 // with an empty function :D
41 const orig = window.require
42 window.require = Object.assign((arg, ...rest) => {
43 if (Array.isArray(arg) && arg[0].indexOf('http') !== 0) {
44 return orig(arg, () => {
45 /* ... */
46 resolve()
47 })
48 }
49 return orig(arg, ...rest)
50 }, orig)
51 const origDefine = window.define
52 window.define = Object.assign((name, dependencies, ...args) => {
53 if (name === 'async') {
54 return origDefine('async', {
55 load: (name, parent, onload) => onload({})
56 })
57 }
58 if (Array.isArray(dependencies)) {
59 dependencies = dependencies.map((dep) => dep === 'recaptcha' ? 'module' : dep)
60 }
61 return origDefine(name, dependencies, ...args)
62 }, origDefine)
63 clearInterval(intv)
64 }
65 }
66 })
67}
68
69const PLUG_ROOM_URL = 'https://plug.dj/plug-socket-test'
70
71// Create a name mapping from plug.dj's obfuscated require.js module names to
72// readable names by running plug-modules in a headless browser-like
73// environment (aka jsdom).
74// You need to be logged in to run plug-modules, so pass in a cookie jar with
75// a valid session cookie.
76module.exports = function createMapping (cookie, ctx) {
77 const reqAsync = (req, id) => new Promise((resolve, reject) => req(id, resolve, reject))
78
79 return new Listr([
80 {
81 title: 'Loading plug.dj',
82 task: (ctx) =>
83 got(PLUG_ROOM_URL, {
84 headers: { cookie: cookie }
85 }).then((response) => {
86 ctx.source = response.body
87 })
88 },
89 {
90 title: 'Opening plug.dj',
91 task: (ctx) => {
92 ctx.dom = new JSDOM(ctx.source, {
93 url: PLUG_ROOM_URL,
94 runScripts: 'dangerously',
95 resources: 'usable',
96 beforeParse (window) {
97 Object.assign(window, stubs)
98 ctx.window = window
99 }
100 })
101 }
102 },
103 {
104 title: 'Injecting module mapping helper',
105 task: (ctx) =>
106 readFile(require.resolve('./get-mapping'), 'utf8').then((source) => {
107 ctx.window.eval(source)
108 })
109 },
110 {
111 title: 'Waiting for plug.dj to finish loading',
112 task: (ctx) => waitForRequireJs(ctx.window)
113 },
114 {
115 title: 'Running plug-modules',
116 task: (ctx) => Promise.resolve(readFile(pmPath, 'utf-8'))
117 // ensure that plugModules defines itself as "plug-modules"
118 .then((plugModules) => plugModules.replace('define([', 'define("plug-modules",['))
119 // insert plug-modules
120 .then((src) => ctx.window.eval(src))
121 .then(() => reqAsync(ctx.window.requirejs, [ 'plug-modules' ]))
122 .then((pm) => ctx.window.getMapping(pm))
123 .tap(() => ctx.window.close())
124 .then(JSON.parse)
125 .then((result) => {
126 ctx.window = null
127 ctx.mapping = result.mapping
128 ctx.appUrl = result.appUrl
129 })
130 }
131 ], { context: ctx })
132}