diff --git a/.gitignore b/.gitignore index 017354f..8f5c351 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ node_modules/* !node_modules/cool-styles -lib diff --git a/lib/file-system-loader.js b/lib/file-system-loader.js new file mode 100644 index 0000000..7743a1e --- /dev/null +++ b/lib/file-system-loader.js @@ -0,0 +1,98 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +var _indexJs = require('./index.js'); + +var _indexJs2 = _interopRequireDefault(_indexJs); + +var _fs = require('fs'); + +var _fs2 = _interopRequireDefault(_fs); + +var _path = require('path'); + +var _path2 = _interopRequireDefault(_path); + +// Sorts dependencies in the following way: +// AAA comes before AA and A +// AB comes after AA and before A +// All Bs come after all As +// This ensures that the files are always returned in the following order: +// - In the order they were required, except +// - After all their dependencies +var traceKeySorter = function traceKeySorter(a, b) { + if (a.length < b.length) { + return a < b.substring(0, a.length) ? -1 : 1; + } else if (a.length > b.length) { + return a.substring(0, b.length) <= b ? -1 : 1; + } else { + return a < b ? -1 : 1; + } +}; + +var FileSystemLoader = (function () { + function FileSystemLoader(root, plugins) { + _classCallCheck(this, FileSystemLoader); + + this.root = root; + this.sources = {}; + this.importNr = 0; + this.core = new _indexJs2['default'](plugins); + this.tokensByFile = {}; + } + + _createClass(FileSystemLoader, [{ + key: 'fetch', + value: function fetch(_newPath, relativeTo, _trace) { + var _this = this; + + var newPath = _newPath.replace(/^["']|["']$/g, ""), + trace = _trace || String.fromCharCode(this.importNr++); + return new Promise(function (resolve, reject) { + var relativeDir = _path2['default'].dirname(relativeTo), + rootRelativePath = _path2['default'].resolve(relativeDir, newPath), + fileRelativePath = _path2['default'].resolve(_path2['default'].join(_this.root, relativeDir), newPath); + + var tokens = _this.tokensByFile[fileRelativePath]; + if (tokens) { + return resolve(tokens); + } + + _fs2['default'].readFile(fileRelativePath, "utf-8", function (err, source) { + if (err) reject(err); + _this.core.load(source, rootRelativePath, trace, _this.fetch.bind(_this)).then(function (_ref) { + var injectableSource = _ref.injectableSource; + var exportTokens = _ref.exportTokens; + + _this.sources[trace] = injectableSource; + _this.tokensByFile[fileRelativePath] = exportTokens; + resolve(exportTokens); + }, reject); + }); + }); + } + }, { + key: 'finalSource', + get: function get() { + var _this2 = this; + + return Object.keys(this.sources).sort(traceKeySorter).map(function (s) { + return _this2.sources[s]; + }).join(""); + } + }]); + + return FileSystemLoader; +})(); + +exports['default'] = FileSystemLoader; +module.exports = exports['default']; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..8c5e8b5 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,67 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +var _postcss = require('postcss'); + +var _postcss2 = _interopRequireDefault(_postcss); + +var _postcssModulesLocalByDefault = require('postcss-modules-local-by-default'); + +var _postcssModulesLocalByDefault2 = _interopRequireDefault(_postcssModulesLocalByDefault); + +var _postcssModulesExtractImports = require('postcss-modules-extract-imports'); + +var _postcssModulesExtractImports2 = _interopRequireDefault(_postcssModulesExtractImports); + +var _postcssModulesScope = require('postcss-modules-scope'); + +var _postcssModulesScope2 = _interopRequireDefault(_postcssModulesScope); + +var _postcssModulesValues = require('postcss-modules-values'); + +var _postcssModulesValues2 = _interopRequireDefault(_postcssModulesValues); + +var _parser = require('./parser'); + +var _parser2 = _interopRequireDefault(_parser); + +var Core = (function () { + function Core(plugins) { + _classCallCheck(this, Core); + + this.plugins = plugins || Core.defaultPlugins; + } + + // These four plugins are aliased under this package for simplicity. + + _createClass(Core, [{ + key: 'load', + value: function load(sourceString, sourcePath, trace, pathFetcher) { + var parser = new _parser2['default'](pathFetcher, trace); + + return (0, _postcss2['default'])(this.plugins.concat([parser.plugin])).process(sourceString, { from: "/" + sourcePath }).then(function (result) { + return { injectableSource: result.css, exportTokens: parser.exportTokens }; + }); + } + }]); + + return Core; +})(); + +exports['default'] = Core; +Core.values = _postcssModulesValues2['default']; +Core.localByDefault = _postcssModulesLocalByDefault2['default']; +Core.extractImports = _postcssModulesExtractImports2['default']; +Core.scope = _postcssModulesScope2['default']; + +Core.defaultPlugins = [_postcssModulesValues2['default'], _postcssModulesLocalByDefault2['default'], _postcssModulesExtractImports2['default'], _postcssModulesScope2['default']]; +module.exports = exports['default']; \ No newline at end of file diff --git a/lib/parser.js b/lib/parser.js new file mode 100644 index 0000000..efa5eee --- /dev/null +++ b/lib/parser.js @@ -0,0 +1,107 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var _icssReplaceSymbols = require('icss-replace-symbols'); + +var _icssReplaceSymbols2 = _interopRequireDefault(_icssReplaceSymbols); + +var importRegexp = /^:import\((.+)\)$/; + +var Parser = (function () { + function Parser(pathFetcher, trace) { + _classCallCheck(this, Parser); + + this.pathFetcher = pathFetcher; + this.plugin = this.plugin.bind(this); + this.exportTokens = {}; + this.translations = {}; + this.trace = trace; + } + + _createClass(Parser, [{ + key: "plugin", + value: function plugin(css, result) { + var _this = this; + + return Promise.all(this.fetchAllImports(css)).then(function (_) { + return _this.linkImportedSymbols(css); + }).then(function (_) { + return _this.extractExports(css); + }); + } + }, { + key: "fetchAllImports", + value: function fetchAllImports(css) { + var _this2 = this; + + var imports = []; + css.each(function (node) { + if (node.type == "rule" && node.selector.match(importRegexp)) { + imports.push(_this2.fetchImport(node, css.source.input.from, imports.length)); + } + }); + return imports; + } + }, { + key: "linkImportedSymbols", + value: function linkImportedSymbols(css) { + (0, _icssReplaceSymbols2["default"])(css, this.translations); + } + }, { + key: "extractExports", + value: function extractExports(css) { + var _this3 = this; + + css.each(function (node) { + if (node.type == "rule" && node.selector == ":export") _this3.handleExport(node); + }); + } + }, { + key: "handleExport", + value: function handleExport(exportNode) { + var _this4 = this; + + exportNode.each(function (decl) { + if (decl.type == 'decl') { + Object.keys(_this4.translations).forEach(function (translation) { + decl.value = decl.value.replace(translation, _this4.translations[translation]); + }); + _this4.exportTokens[decl.prop] = decl.value; + } + }); + exportNode.remove(); + } + }, { + key: "fetchImport", + value: function fetchImport(importNode, relativeTo, depNr) { + var _this5 = this; + + var file = importNode.selector.match(importRegexp)[1], + depTrace = this.trace + String.fromCharCode(depNr); + return this.pathFetcher(file, relativeTo, depTrace).then(function (exports) { + importNode.each(function (decl) { + if (decl.type == 'decl') { + _this5.translations[decl.prop] = exports[decl.value]; + } + }); + importNode.remove(); + }, function (err) { + return console.log(err); + }); + } + }]); + + return Parser; +})(); + +exports["default"] = Parser; +module.exports = exports["default"]; \ No newline at end of file diff --git a/src/file-system-loader.js b/src/file-system-loader.js index 2a3f47d..2f9ab02 100644 --- a/src/file-system-loader.js +++ b/src/file-system-loader.js @@ -36,14 +36,6 @@ export default class FileSystemLoader { rootRelativePath = path.resolve( relativeDir, newPath ), fileRelativePath = path.resolve( path.join( this.root, relativeDir ), newPath ) - // if the path is not relative or absolute, try to resolve it in node_modules - if (newPath[0] !== '.' && newPath[0] !== '/') { - try { - fileRelativePath = require.resolve(newPath); - } - catch (e) {} - } - const tokens = this.tokensByFile[fileRelativePath] if (tokens) { return resolve(tokens) }