Skip to content

Commit

Permalink
fix: atob browser compatible (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
popomore committed Apr 15, 2018
1 parent 2475b7c commit 6d8793b
Show file tree
Hide file tree
Showing 13 changed files with 140 additions and 3 deletions.
21 changes: 20 additions & 1 deletion lib/assets_context.js
Expand Up @@ -4,6 +4,25 @@ const assert = require('assert');
const utility = require('utility');

const CONTEXT_TEMPLATE_ID = 'context' + utility.sha1(String(Date.now()));
// https://github.com/davidchambers/Base64.js/blob/master/base64.js
const atobScript = `
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var atob = window.atob || function(input) {
var str = String(input).replace(/[=]+$/, '');
if (str.length % 4 == 1) {
throw new Error('atob failed: The string to be decoded is not correctly encoded.');
}
for (
var bc = 0, bs, buffer, idx = 0, output = '';
buffer = str.charAt(idx++);
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
buffer = chars.indexOf(buffer);
}
return output;
};
`;

// URL consists of host, resourceBase and entry,
// E.X. http://127.0.0.1:7001/public/index.js
Expand Down Expand Up @@ -48,7 +67,7 @@ class Assets {
getContext(data) {
data = safeStringify(data || this.assetsContext);
let ret = `<div id="${CONTEXT_TEMPLATE_ID}" style="display:none">${data}</div>\n`;
ret += `<script>window.${this.config.contextKey} = JSON.parse(decodeURIComponent(window.atob(document.getElementById('${CONTEXT_TEMPLATE_ID}').textContent)) || '{}');</script>`;
ret += `<script>(function(){${atobScript}window.${this.config.contextKey} = JSON.parse(decodeURIComponent(atob(document.getElementById('${CONTEXT_TEMPLATE_ID}').textContent)) || '{}');})()</script>`;
return ret;
}

Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -35,6 +35,7 @@
"egg-view-nunjucks": "^2.1.6",
"eslint": "^4.19.1",
"eslint-config-egg": "^7.0.0",
"puppeteer": "^1.3.0",
"supertest": "^3.0.0",
"webstorm-disable-index": "^1.2.0"
},
Expand Down
4 changes: 2 additions & 2 deletions test/assets.test.js
Expand Up @@ -239,7 +239,7 @@ describe('test/assets.test.js', () => {
it('should GET /', () => {
return app.httpRequest()
.get('/')
.expect(/<script>window.__context__ =/)
.expect(/window.__context__ =/)
.expect(200);
});
});
Expand All @@ -259,7 +259,7 @@ describe('test/assets.test.js', () => {
return app.httpRequest()
.get('/?query=<x%E2%80%A8x>')
.expect(/<div id="[^"]+" style="display:none">JTdCJTIycXVlcnklMjIlM0ElMjIlM0N4JUUyJTgwJUE4eCUzRSUyMiU3RA==<\/div>/)
.expect(/window\.context = JSON\.parse\(decodeURIComponent\(window\.atob\(document\.getElementById\('[^']+'\).textContent\)\) \|\| '\{\}'\);/)
.expect(/window\.context = JSON\.parse\(decodeURIComponent\(atob\(document\.getElementById\('[^']+'\).textContent\)\) \|\| '\{\}'\);/)
.expect(200);
});
});
Expand Down
13 changes: 13 additions & 0 deletions test/fixtures/apps/ui/app/controller/home.js
@@ -0,0 +1,13 @@
'use strict';

const Controller = require('egg').Controller;

class HomeController extends Controller {
async index() {
await this.ctx.render('index.js', {
data: 1,
});
}
}

module.exports = HomeController;
4 changes: 4 additions & 0 deletions test/fixtures/apps/ui/app/public/index.js
@@ -0,0 +1,4 @@
'use strict';

/* global window */
console.log('data:', window.context.data);
7 changes: 7 additions & 0 deletions test/fixtures/apps/ui/app/router.js
@@ -0,0 +1,7 @@
'use strict';

module.exports = app => {
const { router, controller } = app;

router.get('/', controller.home.index);
};
Empty file.
12 changes: 12 additions & 0 deletions test/fixtures/apps/ui/app/view/layout.html
@@ -0,0 +1,12 @@
<!doctype html>
<html>
<head>
{{ helper.assets.getStyle() | safe }}
</head>
<body>
<div id="root"></div>
<script>window.atob = undefined;</script>
{{ helper.assets.getContext() | safe }}
{{ helper.assets.getScript() | safe }}
</body>
</html>
16 changes: 16 additions & 0 deletions test/fixtures/apps/ui/config/config.default.js
@@ -0,0 +1,16 @@
'use strict';

const path = require('path');

exports.keys = '123456';
exports.view = {
mapping: {
'.js': 'assets',
'.jsx': 'assets',
},
};
exports.assets = {
publicPath: '/public/',
templateViewEngine: 'nunjucks',
templatePath: path.join(__dirname, '../app/view/layout.html'),
};
4 changes: 4 additions & 0 deletions test/fixtures/apps/ui/config/manifest.json
@@ -0,0 +1,4 @@
{
"index.js": "index.js",
"index.css": "index.css"
}
8 changes: 8 additions & 0 deletions test/fixtures/apps/ui/config/plugin.js
@@ -0,0 +1,8 @@
'use strict';

module.exports = {
nunjucks: {
enable: true,
package: 'egg-view-nunjucks',
},
};
3 changes: 3 additions & 0 deletions test/fixtures/apps/ui/package.json
@@ -0,0 +1,3 @@
{
"name": "egg-view-assets"
}
50 changes: 50 additions & 0 deletions test/ui.test.js
@@ -0,0 +1,50 @@
'use strict';

const mock = require('egg-mock');
const puppeteer = require('puppeteer');
const sleep = require('mz-modules/sleep');
const assert = require('assert');


describe('test/ui.test.js', () => {
let app;
before(() => {
mock.env('default');
app = mock.cluster({
baseDir: 'apps/ui',
port: 7001,
});
app.debug();
return app.ready();
});
after(() => app.close());
after(mock.restore);

it('should request index.js', async () => {
await app.httpRequest()
.get('/public/index.js')
.expect(/window.context.data/)
.expect(200);
});

it('should render html', async () => {
await app.httpRequest()
.get('/')
.expect(/var atob = window.atob || function\(input\) {/)
.expect(200);
});

it('should console', async () => {
const browser = await puppeteer.launch({
args: [ '--no-sandbox', '--disable-setuid-sandbox' ],
});
const page = await browser.newPage();
let text = '';
page.on('console', msg => (text += msg.text()));
await page.goto('http://127.0.0.1:7001');

await sleep(5000);
assert(text === 'data: 1');
});

});

0 comments on commit 6d8793b

Please sign in to comment.