import-manager.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. var contexts = require("./contexts"),
  2. Parser = require('./parser/parser'),
  3. FunctionImporter = require('./plugins/function-importer');
  4. module.exports = function(environment) {
  5. // FileInfo = {
  6. // 'relativeUrls' - option - whether to adjust URL's to be relative
  7. // 'filename' - full resolved filename of current file
  8. // 'rootpath' - path to append to normal URLs for this node
  9. // 'currentDirectory' - path to the current file, absolute
  10. // 'rootFilename' - filename of the base file
  11. // 'entryPath' - absolute path to the entry file
  12. // 'reference' - whether the file should not be output and only output parts that are referenced
  13. var ImportManager = function(context, rootFileInfo) {
  14. this.rootFilename = rootFileInfo.filename;
  15. this.paths = context.paths || []; // Search paths, when importing
  16. this.contents = {}; // map - filename to contents of all the files
  17. this.contentsIgnoredChars = {}; // map - filename to lines at the beginning of each file to ignore
  18. this.mime = context.mime;
  19. this.error = null;
  20. this.context = context;
  21. // Deprecated? Unused outside of here, could be useful.
  22. this.queue = []; // Files which haven't been imported yet
  23. this.files = {}; // Holds the imported parse trees.
  24. };
  25. /**
  26. * Add an import to be imported
  27. * @param path - the raw path
  28. * @param tryAppendLessExtension - whether to try appending the less extension (if the path has no extension)
  29. * @param currentFileInfo - the current file info (used for instance to work out relative paths)
  30. * @param importOptions - import options
  31. * @param callback - callback for when it is imported
  32. */
  33. ImportManager.prototype.push = function (path, tryAppendLessExtension, currentFileInfo, importOptions, callback) {
  34. var importManager = this;
  35. this.queue.push(path);
  36. var fileParsedFunc = function (e, root, fullPath) {
  37. importManager.queue.splice(importManager.queue.indexOf(path), 1); // Remove the path from the queue
  38. var importedEqualsRoot = fullPath === importManager.rootFilename;
  39. if (importOptions.optional && e) {
  40. callback(null, {rules:[]}, false, null);
  41. }
  42. else {
  43. importManager.files[fullPath] = root;
  44. if (e && !importManager.error) { importManager.error = e; }
  45. callback(e, root, importedEqualsRoot, fullPath);
  46. }
  47. };
  48. var newFileInfo = {
  49. relativeUrls: this.context.relativeUrls,
  50. entryPath: currentFileInfo.entryPath,
  51. rootpath: currentFileInfo.rootpath,
  52. rootFilename: currentFileInfo.rootFilename
  53. };
  54. var fileManager = environment.getFileManager(path, currentFileInfo.currentDirectory, this.context, environment);
  55. if (!fileManager) {
  56. fileParsedFunc({ message: "Could not find a file-manager for " + path });
  57. return;
  58. }
  59. if (tryAppendLessExtension) {
  60. path = fileManager.tryAppendExtension(path, importOptions.plugin ? ".js" : ".less");
  61. }
  62. var loadFileCallback = function(loadedFile) {
  63. var resolvedFilename = loadedFile.filename,
  64. contents = loadedFile.contents.replace(/^\uFEFF/, '');
  65. // Pass on an updated rootpath if path of imported file is relative and file
  66. // is in a (sub|sup) directory
  67. //
  68. // Examples:
  69. // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/',
  70. // then rootpath should become 'less/module/nav/'
  71. // - If path of imported file is '../mixins.less' and rootpath is 'less/',
  72. // then rootpath should become 'less/../'
  73. newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename);
  74. if (newFileInfo.relativeUrls) {
  75. newFileInfo.rootpath = fileManager.join(
  76. (importManager.context.rootpath || ""),
  77. fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
  78. if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) {
  79. newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath);
  80. }
  81. }
  82. newFileInfo.filename = resolvedFilename;
  83. var newEnv = new contexts.Parse(importManager.context);
  84. newEnv.processImports = false;
  85. importManager.contents[resolvedFilename] = contents;
  86. if (currentFileInfo.reference || importOptions.reference) {
  87. newFileInfo.reference = true;
  88. }
  89. if (importOptions.plugin) {
  90. new FunctionImporter(newEnv, newFileInfo).eval(contents, function (e, root) {
  91. fileParsedFunc(e, root, resolvedFilename);
  92. });
  93. } else if (importOptions.inline) {
  94. fileParsedFunc(null, contents, resolvedFilename);
  95. } else {
  96. new Parser(newEnv, importManager, newFileInfo).parse(contents, function (e, root) {
  97. fileParsedFunc(e, root, resolvedFilename);
  98. });
  99. }
  100. };
  101. var promise = fileManager.loadFile(path, currentFileInfo.currentDirectory, this.context, environment,
  102. function(err, loadedFile) {
  103. if (err) {
  104. fileParsedFunc(err);
  105. } else {
  106. loadFileCallback(loadedFile);
  107. }
  108. });
  109. if (promise) {
  110. promise.then(loadFileCallback, fileParsedFunc);
  111. }
  112. };
  113. return ImportManager;
  114. };