Skip to content

Commit

Permalink
Merge pull request #1 from htmlacademy/feature/create-worker-from-blob
Browse files Browse the repository at this point in the history
Добавляет задачу и скрипт для создания воркера из строки кода
  • Loading branch information
Nikolay Emrikh authored Jul 28, 2020
2 parents 71d9bed + 36338de commit 73a1291
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 16 deletions.
15 changes: 6 additions & 9 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module.exports = function(grunt) {
// concatenate source files and libsass.js
grunt.registerTask('build:worker', ['concat:worker']);
grunt.registerTask('build:sass', ['concat:sass']);
grunt.registerTask('build:sass-inline-code', ['concat:sassInlineCode']);
grunt.registerTask('build:sync', ['concat:sync']);
grunt.registerTask('build:node', ['concat:node']);

Expand Down Expand Up @@ -54,16 +55,12 @@ module.exports = function(grunt) {
'build:node',
'file-size',
]);
// simplifications for development
grunt.registerTask('rebuild', [
'versions',
'libsass:build',
'build:sass',
'build:worker',
'build:sync',
'build:node',
'file-size',

grunt.registerTask('build:sass:htmlacademy', [
'build:sass-inline-code',
'file-size'
]);

grunt.registerTask('rebuild:debug', [
'versions',
'libsass:debug',
Expand Down
180 changes: 180 additions & 0 deletions dist/sass.inline-code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
(function (root, factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.Sass = factory();
}
}(this, function () {/*global document*/
// identify the path sass.js is located at in case we're loaded by a simple
// <script src="path/to/sass.js"></script>
// this path can be used to identify the location of
// * sass.worker.js from sass.js
// * libsass.js.mem from sass.sync.js
// see https://github.com/medialize/sass.js/pull/32#issuecomment-103142214
// see https://github.com/medialize/sass.js/issues/33
var SASSJS_RELATIVE_PATH = (function() {
'use strict';

// in Node things are rather simple
var hasDir = typeof __dirname !== 'undefined';
if (hasDir) {
return __dirname;
}

// we can only run this test in the browser,
// so make sure we actually have a DOM to work with.
if (typeof document === 'undefined' || !document.getElementsByTagName) {
return null;
}

// http://www.2ality.com/2014/05/current-script.html
var currentScript = document.currentScript || (function() {
var scripts = document.getElementsByTagName('script');
return scripts[scripts.length - 1];
})();

var path = currentScript && currentScript.src;
if (!path) {
return null;
}

// [worker] make sure we're not running in some concatenated thing
if (path.slice(-8) === '/sass.js') {
return path.slice(0, -8);
}

// [sync] make sure we're not running in some concatenated thing
if (path.slice(-13) === '/sass.sync.js') {
return path.slice(0, -13);
}

return null;
})() || '.';

/*global Worker, SASSJS_RELATIVE_PATH*/
'use strict';

var noop = function(){};
var slice = [].slice;

function Sass(workerCode) {
if (!workerCode) {
/*jshint laxbreak:true */
throw new TypeError('Provide code for worker');
/*jshint laxbreak:false */
}

// bind all functions
// we're doing this because we used to have a single hard-wired instance that allowed
// [].map(Sass.removeFile) and we need to maintain that for now (at least until 1.0.0)
for (var key in this) {
if (typeof this[key] === 'function') {
this[key] = this[key].bind(this);
}
}

const blob = new Blob([workerCode], {type: 'application/javascript'});

this._callbacks = {};
this._worker = new Worker(URL.createObjectURL(blob));
this._worker.addEventListener('message', this._handleWorkerMessage, false);
}

Sass.style = {
nested: 0,
expanded: 1,
compact: 2,
compressed: 3
};

Sass.comments = {
'none': 0,
'default': 1
};

Sass.prototype = {
style: Sass.style,
comments: Sass.comments,

destroy: function() {
this._worker && this._worker.terminate();
this._worker = null;
this._callbacks = {};
this._importer = null;
},

_handleWorkerMessage: function(event) {
if (event.data.command) {
this[event.data.command](event.data.args);
}

this._callbacks[event.data.id] && this._callbacks[event.data.id](event.data.result);
delete this._callbacks[event.data.id];
},

_dispatch: function(options, callback) {
if (!this._worker) {
throw new Error('Sass worker has been terminated');
}

options.id = 'cb' + Date.now() + Math.random();
this._callbacks[options.id] = callback;
this._worker.postMessage(options);
},

_importerInit: function(args) {
// importer API done callback pushing results
// back to the worker
var done = function done(result) {
this._worker.postMessage({
command: '_importerFinish',
args: [result]
});
}.bind(this);

try {
this._importer(args[0], done);
} catch(e) {
done({ error: e.message });
throw e;
}
},

importer: function(importerCallback, callback) {
if (typeof importerCallback !== 'function' && importerCallback !== null) {
throw new Error('importer callback must either be a function or null');
}

// callback is executed in the main EventLoop
this._importer = importerCallback;
// tell worker to activate importer callback
this._worker.postMessage({
command: 'importer',
args: [Boolean(importerCallback)]
});

callback && callback();
},
};

var commands = 'writeFile readFile listFiles removeFile clearFiles lazyFiles preloadFiles options compile compileFile';
commands.split(' ').forEach(function(command) {
Sass.prototype[command] = function() {
var callback = slice.call(arguments, -1)[0];
var args = slice.call(arguments, 0, -1);
if (typeof callback !== 'function') {
args.push(callback);
callback = noop;
}

this._dispatch({
command: command,
args: args
}, callback);
};
});
return Sass;
}));
22 changes: 15 additions & 7 deletions grunt-tasks/concat.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ module.exports = function GruntfileConcat(grunt) {
'use strict';

/*jshint laxbreak:true */
var banner = '/*! <%= pkg.name %> - v<%= pkg.version %> (<%= versions.sassjs.commit %>) - built <%= grunt.template.today("yyyy-mm-dd") %>\n'
+ ' providing libsass <%= versions.libsass.version %> (<%= versions.libsass.commit %>)\n'
+ ' via emscripten <%= versions.emscripten.version %> (<%= versions.emscripten.commit %>)\n */\n';
var umdHeader = ['(function (root, factory) {',
' \'use strict\';',
' if (typeof define === \'function\' && define.amd) {',
Expand All @@ -26,7 +23,18 @@ module.exports = function GruntfileConcat(grunt) {
'src/sass.js'
],
options: {
banner: banner + '\n' + umdHeader,
banner: umdHeader,
footer: umdFooter,
}
},
sassInlineCode: {
dest: 'dist/sass.inline-code.js',
src: [
'src/sass.configure.path.js',
'src/sass.inline-code.js'
],
options: {
banner: umdHeader,
footer: umdFooter,
}
},
Expand All @@ -42,7 +50,7 @@ module.exports = function GruntfileConcat(grunt) {
'src/sass.worker.js'
],
options: {
banner: banner,
banner: '',
process: function (content) {
return content
// prevent emscripted libsass from exporting itself
Expand All @@ -64,7 +72,7 @@ module.exports = function GruntfileConcat(grunt) {
'src/sass.resolve-paths.js'
],
options: {
banner: banner + '\n' + umdHeader,
banner: umdHeader,
footer: umdFooter,
process: function (content) {
// prevent emscripted libsass from exporting itself
Expand All @@ -78,7 +86,7 @@ module.exports = function GruntfileConcat(grunt) {
'src/sass.node.js',
],
options: {
banner: banner,
banner: '',
},
},
});
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
],
"main": "./dist/sass.sync.js",
"scripts": {
"build:sass:htmlacademy": "grunt build:sass:htmlacademy",
"build": "grunt build",
"build:debug": "grunt build:debug",
"rebuild": "grunt rebuild",
Expand Down
122 changes: 122 additions & 0 deletions src/sass.inline-code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*global Worker, SASSJS_RELATIVE_PATH*/
'use strict';

var noop = function(){};
var slice = [].slice;

function Sass(workerCode) {
if (!workerCode) {
/*jshint laxbreak:true */
throw new TypeError('Provide code for worker');
/*jshint laxbreak:false */
}

// bind all functions
// we're doing this because we used to have a single hard-wired instance that allowed
// [].map(Sass.removeFile) and we need to maintain that for now (at least until 1.0.0)
for (var key in this) {
if (typeof this[key] === 'function') {
this[key] = this[key].bind(this);
}
}

const blob = new Blob([workerCode], {type: 'application/javascript'});

this._callbacks = {};
this._worker = new Worker(URL.createObjectURL(blob));
this._worker.addEventListener('message', this._handleWorkerMessage, false);
}

Sass.style = {
nested: 0,
expanded: 1,
compact: 2,
compressed: 3
};

Sass.comments = {
'none': 0,
'default': 1
};

Sass.prototype = {
style: Sass.style,
comments: Sass.comments,

destroy: function() {
this._worker && this._worker.terminate();
this._worker = null;
this._callbacks = {};
this._importer = null;
},

_handleWorkerMessage: function(event) {
if (event.data.command) {
this[event.data.command](event.data.args);
}

this._callbacks[event.data.id] && this._callbacks[event.data.id](event.data.result);
delete this._callbacks[event.data.id];
},

_dispatch: function(options, callback) {
if (!this._worker) {
throw new Error('Sass worker has been terminated');
}

options.id = 'cb' + Date.now() + Math.random();
this._callbacks[options.id] = callback;
this._worker.postMessage(options);
},

_importerInit: function(args) {
// importer API done callback pushing results
// back to the worker
var done = function done(result) {
this._worker.postMessage({
command: '_importerFinish',
args: [result]
});
}.bind(this);

try {
this._importer(args[0], done);
} catch(e) {
done({ error: e.message });
throw e;
}
},

importer: function(importerCallback, callback) {
if (typeof importerCallback !== 'function' && importerCallback !== null) {
throw new Error('importer callback must either be a function or null');
}

// callback is executed in the main EventLoop
this._importer = importerCallback;
// tell worker to activate importer callback
this._worker.postMessage({
command: 'importer',
args: [Boolean(importerCallback)]
});

callback && callback();
},
};

var commands = 'writeFile readFile listFiles removeFile clearFiles lazyFiles preloadFiles options compile compileFile';
commands.split(' ').forEach(function(command) {
Sass.prototype[command] = function() {
var callback = slice.call(arguments, -1)[0];
var args = slice.call(arguments, 0, -1);
if (typeof callback !== 'function') {
args.push(callback);
callback = noop;
}

this._dispatch({
command: command,
args: args
}, callback);
};
});

0 comments on commit 73a1291

Please sign in to comment.