Skip to content

Commit

Permalink
feat(useable): add insertInto support (options.insertInto) (#341)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonenglish authored and michael-ciniawsky committed Aug 27, 2018
1 parent 4217bd1 commit 2588aca
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 19 deletions.
142 changes: 124 additions & 18 deletions test/useable.test.js
Expand Up @@ -8,30 +8,136 @@ var loaderUtils = require('loader-utils');
var useable = require("../useable");

describe("useable tests", function () {
var sandbox = sinon.sandbox.create();
var getOptions;
describe('hmr', function () {
var sandbox = sinon.sandbox.create();
var getOptions;

beforeEach(() => {
// Mock loaderUtils to override options
getOptions = sandbox.stub(loaderUtils, 'getOptions');
});
beforeEach(() => {
// Mock loaderUtils to override options
getOptions = sandbox.stub(loaderUtils, 'getOptions');
});

afterEach(() => {
sandbox.restore();
});
afterEach(() => {
sandbox.restore();
});

it("should output HMR code by default", function () {
assert.equal(/(module\.hot)/g.test(useable.pitch()), true);
});
it("should output HMR code by default", function () {
assert.equal(/(module\.hot)/g.test(useable.pitch()), true);
});

it("should NOT output HMR code when options.hmr is false", function () {
getOptions.returns({hmr: false});
assert.equal(/(module\.hot)/g.test(useable.pitch()), false);
it("should NOT output HMR code when options.hmr is false", function () {
getOptions.returns({hmr: false});
assert.equal(/(module\.hot)/g.test(useable.pitch()), false);
});

it("should output HMR code when options.hmr is true", function () {
getOptions.returns({hmr: true});
assert.equal(/(module\.hot)/g.test(useable.pitch()), true);
});
});

it("should output HMR code when options.hmr is true", function () {
getOptions.returns({hmr: true});
assert.equal(/(module\.hot)/g.test(useable.pitch()), true);
describe('insert into', function () {
var path = require("path");

var utils = require("./utils"),
runCompilerTest = utils.runCompilerTest;

var fs;

var requiredCss = ".required { color: blue }",
requiredCssTwo = ".requiredTwo { color: cyan }",
localScopedCss = ":local(.className) { background: red; }",
localComposingCss = `
:local(.composingClass) {
composes: className from './localScoped.css';
color: blue;
}
`,
requiredStyle = `<style type="text/css">${requiredCss}</style>`,
existingStyle = `<style id="existing-style">.existing { color: yellow }</style>`,
checkValue = '<div class="check">check</div>',
rootDir = path.resolve(__dirname + "/../") + "/",
jsdomHtml = [
"<html>",
"<head id='head'>",
existingStyle,
"</head>",
"<body>",
"<div class='target'>",
checkValue,
"</div>",
"<iframe class='iframeTarget'/>",
"</body>",
"</html>"
].join("\n"),
requiredJS = [
"var el = document.createElement('div');",
"el.id = \"test-shadow\";",
"document.body.appendChild(el)",
"var css = require('./style.css');",
"css.use();",
].join("\n");

var styleLoaderOptions = {};
var cssRule = {};

var defaultCssRule = {
test: /\.css?$/,
use: [
{
loader: "style-loader/useable",
options: styleLoaderOptions
},
"css-loader"
]
};

var webpackConfig = {
entry: "./main.js",
output: {
filename: "bundle.js"
},
module: {
rules: [cssRule]
}
};

var setupWebpackConfig = function() {
fs = utils.setup(webpackConfig, jsdomHtml);

// Create a tiny file system. rootDir is used because loaders are referring to absolute paths.
fs.mkdirpSync(rootDir);
fs.writeFileSync(rootDir + "main.js", requiredJS);
fs.writeFileSync(rootDir + "style.css", requiredCss);
fs.writeFileSync(rootDir + "styleTwo.css", requiredCssTwo);
fs.writeFileSync(rootDir + "localScoped.css", localScopedCss);
fs.writeFileSync(rootDir + "localComposing.css", localComposingCss);
};

beforeEach(function() {
// Reset all style-loader options
for (var member in styleLoaderOptions) {
delete styleLoaderOptions[member];
}

for (var member in defaultCssRule) {
cssRule[member] = defaultCssRule[member];
}

setupWebpackConfig();
}); // before each

it("insert into iframe", function(done) {
let selector = "iframe.iframeTarget";
styleLoaderOptions.insertInto = selector;

let expected = requiredStyle;

runCompilerTest(expected, done, function() {
return this.document.querySelector(selector).contentDocument.head.innerHTML;
}, selector);
}); // it insert into

});

});
19 changes: 18 additions & 1 deletion useable.js
Expand Up @@ -18,6 +18,21 @@ module.exports.pitch = function (request) {

options.hmr = typeof options.hmr === 'undefined' ? true : options.hmr;

// The variable is needed, because the function should be inlined.
// If is just stored it in options, JSON.stringify will quote
// the function and it would be just a string at runtime
var insertInto;

if (typeof options.insertInto === "function") {
insertInto = options.insertInto.toString();
}

// We need to check if it a string, or variable will be "undefined"
// and the loader crashes
if (typeof options.insertInto === "string") {
insertInto = '"' + options.insertInto + '"';
}

var hmr = [
// Hot Module Replacement
"if(module.hot) {",
Expand Down Expand Up @@ -48,14 +63,16 @@ module.exports.pitch = function (request) {
"var refs = 0;",
"var dispose;",
"var content = require(" + loaderUtils.stringifyRequest(this, "!!" + request) + ");",
"var options = " + JSON.stringify(options) + ";",
"options.insertInto = " + insertInto + ";",
"",
"if(typeof content === 'string') content = [[module.id, content, '']];",
// Export CSS Modules
"if(content.locals) exports.locals = content.locals;",
"",
"exports.use = exports.ref = function() {",
" if(!(refs++)) {",
" dispose = require(" + loaderUtils.stringifyRequest(this, "!" + path.join(__dirname, "lib", "addStyles.js")) + ")(content, " + JSON.stringify(options) + ");",
" dispose = require(" + loaderUtils.stringifyRequest(this, "!" + path.join(__dirname, "lib", "addStyles.js")) + ")(content, options);",
" }",
"",
" return exports;",
Expand Down

0 comments on commit 2588aca

Please sign in to comment.