fix(client): prevent endless SASS conversion

This commit is contained in:
Valeriy 2018-11-11 16:49:20 +03:00 committed by Stuart Taylor
parent 39e936bbaa
commit ecadba95db
7 changed files with 77 additions and 34 deletions

5
client/.gitignore vendored
View File

@ -9,6 +9,5 @@ config/env.json
/public
.DS_Store
./static/js/frame-runner.js
./static/js/frame-runner.js.map
./static/_redirects
/static/js
./static/_redirects

View File

@ -0,0 +1,11 @@
/* eslint-disable */
importScripts(
'https://cdnjs.cloudflare.com/ajax/libs/sass.js/0.10.11/sass.sync.min.js'
);
onmessage = e => {
const data = e.data;
Sass.compile(data, result => {
self.postMessage(result.text);
});
}

View File

@ -1,11 +1,10 @@
import favicons from './favicons';
import meta from './meta';
import mathjax from './mathjax';
import sassjs from './sassjs';
import scripts from './scripts';
const metaAndStyleSheets = meta
.concat(favicons, mathjax, sassjs, scripts)
.concat(favicons, mathjax, scripts)
.map((element, i) => ({
...element,
key: `meta-stylesheet-${i}`,

View File

@ -1,8 +0,0 @@
import React from 'react';
const cdnAddr =
'https://cdnjs.cloudflare.com/ajax/libs/sass.js/0.10.9/sass.sync.min.js';
const sassjs = [<script key='sassjs' src={cdnAddr} type='text/javascript' />];
export default sassjs;

View File

@ -16,6 +16,7 @@ import presetReact from '@babel/preset-react';
import protect from 'loop-protect';
import * as vinyl from '../utils/polyvinyl.js';
import WorkerExecutor from '../utils/worker-executor';
const protectTimeout = 100;
Babel.registerPlugin('loopProtection', protect(protectTimeout));
@ -74,27 +75,21 @@ export const babelTransformer = cond([
[stubTrue, identity]
]);
const sass = new WorkerExecutor('sass');
const htmlSassTransformCode = file => {
let doc = document.implementation.createHTMLDocument();
doc.body.innerHTML = file.contents;
let styleTags = [].filter.call(
doc.querySelectorAll('style'),
style => style.type === 'text/sass'
);
if (styleTags.length === 0 || typeof Sass === 'undefined') {
return vinyl.transformContents(() => doc.body.innerHTML, file);
const div = document.createElement('div');
div.innerHTML = file.contents;
const styleTags = div.querySelectorAll('style[type="text/sass"]');
if (styleTags.length > 0) {
return Promise.all([].map.call(styleTags, async style => {
style.type = 'text/css';
style.innerHTML = await sass.execute(style.innerHTML, 2000);
})).then(() => (
vinyl.transformContents(() => div.innerHTML, file)
));
}
return Promise.all(styleTags.map(style => (
new Promise(resolve => {
window.Sass.compile(style.innerHTML, function(result) {
style.type = 'text/css';
style.innerHTML = result.text;
resolve();
});
})
))).then(() => (
vinyl.transformContents(() => doc.body.innerHTML, file)
));
return vinyl.transformContents(() => div.innerHTML, file);
};
export const htmlSassTransformer = cond([

View File

@ -0,0 +1,45 @@
export default class WorkerExecutor {
constructor(workerName) {
this.workerName = workerName;
this.worker = null;
}
getWorker() {
if (this.worker === null) {
this.worker = new Worker(`js/${this.workerName}.js`);
}
return this.worker;
}
killWorker() {
if (this.worker !== null) {
this.worker.terminate();
this.worker = null;
}
}
execute(data, timeout = 1000) {
const worker = this.getWorker();
return new Promise((resolve, reject) => {
// Handle timeout
const timeoutId = setTimeout(() => {
this.killWorker();
reject('timeout');
}, timeout);
worker.postMessage(data);
// Handle result
worker.onmessage = e => {
clearTimeout(timeoutId);
resolve(e.data);
};
worker.onerror = e => {
clearTimeout(timeoutId);
reject(e.message);
};
});
}
}

View File

@ -4,10 +4,12 @@ module.exports = (env = {}) => {
const __DEV__ = env.production !== true;
return {
mode: __DEV__ ? 'development' : 'production',
entry: './src/client/frame-runner.js',
entry: {
'frame-runner': './src/client/frame-runner.js',
sass: './src/client/workers/sass.js'
},
devtool: __DEV__ ? 'inline-source-map' : 'source-map',
output: {
filename: 'frame-runner.js',
path: path.join(__dirname, './static/js')
},
stats: {