Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"mocha"
],
"env": {
"node": true
"node": true,
"es6": true
}
}
60 changes: 29 additions & 31 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
const { basename, extname, posix, relative } = require('path');
const { basename, extname, relative } = require('path');
const { compile, preprocess } = require('svelte');
const { getOptions } = require('loader-utils');
const { statSync, utimesSync, writeFileSync } = require('fs');
const { tmpdir } = require('os');
const VirtualModules = require('./lib/virtual');

const hotApi = require.resolve('./lib/hot-api.js');

function makeHot(id, code, hotOptions) {
const options = JSON.stringify(hotOptions);
const replacement = `

if (module.hot) {

const { configure, register, reload } = require('${posixify(hotApi)}');

module.hot.accept();
Expand All @@ -26,7 +23,6 @@ if (module.hot) {
}
}


export default $2;
`;

Expand Down Expand Up @@ -80,7 +76,15 @@ function deprecatePreprocessOptions(options) {
options.preprocess = options.preprocess || preprocessOptions;
}

const virtualModuleInstances = new Map();

module.exports = function(source, map) {
if (this._compiler && !virtualModuleInstances.has(this._compiler)) {
virtualModuleInstances.set(this._compiler, new VirtualModules(this._compiler));
}

const virtualModules = virtualModuleInstances.get(this._compiler);

this.cacheable();

const options = Object.assign({}, this.options, getOptions(this));
Expand All @@ -90,47 +94,41 @@ module.exports = function(source, map) {
const isProduction = this.minimize || process.env.NODE_ENV === 'production';

options.filename = this.resourcePath;
if (!options.format) {
options.format = this.version === 1 ? options.format || 'cjs' : 'es';
}
if (!options.shared) {
options.shared = options.format === 'es' && 'svelte/shared.js';
}

if (!('format' in options)) options.format = 'es';
if (!('shared' in options)) options.shared = options.format === 'es' && 'svelte/shared.js';
if (!('name' in options)) options.name = capitalize(sanitize(options.filename));
if (!('onwarn' in options)) options.onwarn = warning => this.emitWarning(new Error(warning));
if (options.emitCss) options.css = false;

if (!options.name) options.name = capitalize(sanitize(options.filename));

if (!options.onwarn) options.onwarn = warning => this.emitWarning(new Error(warning));

deprecatePreprocessOptions(options);
options.preprocess.filename = options.filename;

preprocess(source, options.preprocess).then(processed => {
let { js, css, ast } = normalize(compile(processed.toString(), options));

if (options.emitCss && css.code) {
const posixTmpdir = posixify(tmpdir());
const tmpFile = posix.join(posixTmpdir, 'svelte-' + ast.hash + '.css');

css.code += '\n/*# sourceMappingURL=' + css.map.toUrl() + '*/';
js.code = js.code + `\nrequire('${tmpFile}');\n`;

writeFileSync(tmpFile, css.code);
const { atime, mtime } = statSync(tmpFile);
utimesSync(tmpFile, new Date(atime.getTime() - 99999), new Date(mtime.getTime() - 99999));
}
let { js, css } = normalize(compile(processed.toString(), options));

if (options.hotReload && !isProduction && !isServer) {
const hotOptions = Object.assign({}, options.hotOptions);
const id = JSON.stringify(relative(process.cwd(), options.filename));
js.code = makeHot(id, js.code, hotOptions);
}

if (options.emitCss && css.code) {
const cssFilepath = options.filename.replace(
/\.[^/.]+$/,
`.svelte.css`
);
css.code += '\n/*# sourceMappingURL=' + css.map.toUrl() + '*/';
js.code = js.code + `\nimport '${cssFilepath}';\n`;

if (virtualModules) {
virtualModules.writeModule(cssFilepath, css.code);
}
}

callback(null, js.code, js.map);
}, err => callback(err)).catch(err => {
// wrap error to provide correct
// context when logging to console
callback(new Error(`${err.name}: ${err.toString()}`));
});
};
};
89 changes: 89 additions & 0 deletions lib/virtual-stats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* Used to cache a stats object for the virtual file.
* Extracted from the `mock-fs` package.
*
* @author Tim Schaub http://tschaub.net/
* @link https://github.com/tschaub/mock-fs/blob/master/lib/binding.js
* @link https://github.com/tschaub/mock-fs/blob/master/license.md
*/

/* eslint-disable no-restricted-syntax, no-prototype-builtins, no-continue */
/* eslint-disable no-bitwise, no-underscore-dangle */

'use strict';

var constants = require('constants');

/**
* Create a new stats object.
* @param {Object} config Stats properties.
* @constructor
*/
function VirtualStats(config) {
for (var key in config) {
if (!config.hasOwnProperty(key)) {
continue;
}
this[key] = config[key];
}
}

/**
* Check if mode indicates property.
* @param {number} property Property to check.
* @return {boolean} Property matches mode.
*/
VirtualStats.prototype._checkModeProperty = function(property) {
return (this.mode & constants.S_IFMT) === property;
};

/**
* @return {Boolean} Is a directory.
*/
VirtualStats.prototype.isDirectory = function() {
return this._checkModeProperty(constants.S_IFDIR);
};

/**
* @return {Boolean} Is a regular file.
*/
VirtualStats.prototype.isFile = function() {
return this._checkModeProperty(constants.S_IFREG);
};

/**
* @return {Boolean} Is a block device.
*/
VirtualStats.prototype.isBlockDevice = function() {
return this._checkModeProperty(constants.S_IFBLK);
};

/**
* @return {Boolean} Is a character device.
*/
VirtualStats.prototype.isCharacterDevice = function() {
return this._checkModeProperty(constants.S_IFCHR);
};

/**
* @return {Boolean} Is a symbolic link.
*/
VirtualStats.prototype.isSymbolicLink = function() {
return this._checkModeProperty(constants.S_IFLNK);
};

/**
* @return {Boolean} Is a named pipe.
*/
VirtualStats.prototype.isFIFO = function() {
return this._checkModeProperty(constants.S_IFIFO);
};

/**
* @return {Boolean} Is a socket.
*/
VirtualStats.prototype.isSocket = function() {
return this._checkModeProperty(constants.S_IFSOCK);
};

module.exports = VirtualStats;
73 changes: 73 additions & 0 deletions lib/virtual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
var VirtualStats = require('./virtual-stats');

var inode = 45000000;

// Adapted from https://github.com/sysgears/webpack-virtual-modules
// MIT Licensed https://github.com/sysgears/webpack-virtual-modules/blob/master/LICENSE

function VirtualModulesPlugin(compiler) {
this.compiler = compiler;

if (!compiler.inputFileSystem._writeVirtualFile) {
var originalPurge = compiler.inputFileSystem.purge;

compiler.inputFileSystem.purge = function() {
originalPurge.call(this, arguments);
if (this._virtualFiles) {
Object.keys(this._virtualFiles).forEach(
function(file) {
var data = this._virtualFiles[file];
setData(this._statStorage, file, [null, data.stats]);
setData(this._readFileStorage, file, [null, data.contents]);
}.bind(this)
);
}
};

compiler.inputFileSystem._writeVirtualFile = function(file, stats, contents) {
this._virtualFiles = this._virtualFiles || {};
this._virtualFiles[file] = { stats: stats, contents: contents };
setData(this._statStorage, file, [null, stats]);
setData(this._readFileStorage, file, [null, contents]);
};
}

compiler.hooks.watchRun.tapAsync('VirtualModulesPlugin', (watcher, callback) => {
this._watcher = watcher.compiler || watcher;
callback();
});
}

VirtualModulesPlugin.prototype.writeModule = function(filePath, contents) {
var len = contents ? contents.length : 0;
var time = Date.now();

var stats = new VirtualStats({
dev: 8675309,
nlink: 0,
uid: 1000,
gid: 1000,
rdev: 0,
blksize: 4096,
ino: inode++,
mode: 33188,
size: len,
blocks: Math.floor(len / 4096),
atime: time,
mtime: time,
ctime: time,
birthtime: time
});

this.compiler.inputFileSystem._writeVirtualFile(filePath, stats, contents);
};

function setData(storage, key, value) {
if (storage.data instanceof Map) {
storage.data.set(key, value);
} else {
storage.data[key] = value;
}
}

module.exports = VirtualModulesPlugin;
Loading