Skip to content

Commit

Permalink
Import file with dots in file name (#3453)
Browse files Browse the repository at this point in the history
* Fix bug with importing file with dots in names.

* Fix tests of importing files in browser and in node.

* Add unit test for bug with importing files with dots in file name.
  • Loading branch information
life777 authored Feb 9, 2020
1 parent b354421 commit fd66e44
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 101 deletions.
65 changes: 43 additions & 22 deletions lib/less-browser/file-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ class FileManager extends AbstractFileManager {
return true;
}

getPossibleFileExtensions(path, ext) {
if (this.isPathWithExtension(path, ext)) {
return [''];
}

// if file doesn't have dot in name it require extention
if (path.indexOf(".") === -1) {
return [ext];
}

// path has dots in name it might be with or without extention (like .css)
return [ext, ''];
}

join(basePath, laterPath) {
if (!basePath) {
return laterPath;
Expand Down Expand Up @@ -57,6 +71,29 @@ class FileManager extends AbstractFileManager {
}
}

tryToLoad(href) {
return new Promise((resolve, reject) => {
if (options.useFileCache && fileCache[href]) {
try {
const lessText = fileCache[href];
return resolve({ contents: lessText, filename: href, webInfo: { lastModified: new Date() }});
} catch (e) {
return reject({ filename: href, message: `Error loading file ${href} error was ${e.message}` });
}
}

this.doXHR(href, options.mime, function doXHRCallback(data, lastModified) {
// per file cache
fileCache[href] = data;

// Use remote copy (re-parse)
resolve({ contents: data, filename: href, webInfo: { lastModified }});
}, function doXHRError(status, url) {
reject({ type: 'File', message: `'${url}' wasn't found (${status})`, href });
});
});
}

supports() {
return true;
}
Expand All @@ -73,36 +110,20 @@ class FileManager extends AbstractFileManager {
filename = currentDirectory + filename;
}

filename = options.ext ? this.tryAppendExtension(filename, options.ext) : filename;
const extensions = this.getPossibleFileExtensions(filename, options.ext || '');

options = options || {};

// sheet may be set to the stylesheet for the initial load or a collection of properties including
// some context variables for imports
const hrefParts = this.extractUrlParts(filename, window.location.href);
const href = hrefParts.url;
const self = this;

return new Promise((resolve, reject) => {
if (options.useFileCache && fileCache[href]) {
try {
const lessText = fileCache[href];
return resolve({ contents: lessText, filename: href, webInfo: { lastModified: new Date() }});
} catch (e) {
return reject({ filename: href, message: `Error loading file ${href} error was ${e.message}` });
}
}

self.doXHR(href, options.mime, function doXHRCallback(data, lastModified) {
// per file cache
fileCache[href] = data;
const hrefs = extensions.map(ext => this.tryAppendExtension(href, ext))

// Use remote copy (re-parse)
resolve({ contents: data, filename: href, webInfo: { lastModified }});
}, function doXHRError(status, url) {
reject({ type: 'File', message: `'${url}' wasn't found (${status})`, href });
});
});
return hrefs.reduce(
(prev, href) => prev.catch(() => this.tryToLoad(href)),
this.tryToLoad(hrefs.shift())
);
}
}

Expand Down
149 changes: 71 additions & 78 deletions lib/less-node/file-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ class FileManager extends AbstractFileManager {
return true;
}

getPossibleFileExtensions(path, ext) {
if (this.isPathWithExtension(path, ext)) {
return [''];
}

return ['', ext];
}

loadFile(filename, currentDirectory, options, environment, callback) {
let fullFilename;
const isAbsoluteFilename = this.isPathAbsolute(filename);
Expand All @@ -37,6 +45,8 @@ class FileManager extends AbstractFileManager {
if (!isAbsoluteFilename && paths.indexOf('.') === -1) { paths.push('.'); }

const prefixes = options.prefixes || [''];

const extensions = this.getPossibleFileExtensions(filename, options.ext);
const fileParts = this.extractUrlParts(filename);

if (options.syncImport) {
Expand Down Expand Up @@ -69,93 +79,76 @@ class FileManager extends AbstractFileManager {
if (i < paths.length) {
(function tryPrefix(j) {
if (j < prefixes.length) {
isNodeModule = false;
fullFilename = fileParts.rawPath + prefixes[j] + fileParts.filename;

if (paths[i]) {
fullFilename = path.join(paths[i], fullFilename);
}
(function tryExtension(k) {
if (k < extensions.length) {
isNodeModule = false;
fullFilename = fileParts.rawPath + prefixes[j] + fileParts.filename + extensions[k];

if (!explicit && paths[i] === '.') {
try {
fullFilename = require.resolve(fullFilename);
isNodeModule = true;
}
catch (e) {
filenamesTried.push(npmPrefix + fullFilename);
tryWithExtension();
}
}
else {
tryWithExtension();
}

function tryWithExtension() {
const extFilename = options.ext ? self.tryAppendExtension(fullFilename, options.ext) : fullFilename;

if (extFilename !== fullFilename && !explicit && paths[i] === '.') {
try {
fullFilename = require.resolve(extFilename);
isNodeModule = true;
}
catch (e) {
filenamesTried.push(npmPrefix + extFilename);
fullFilename = extFilename;
if (paths[i]) {
fullFilename = path.join(paths[i], fullFilename);
}
}
else {
fullFilename = extFilename;
}
}

let modified = false;

if (self.contents[fullFilename]) {
try {
var stat = fs.statSync.apply(this, [fullFilename]);
if (stat.mtime.getTime() === self.contents[fullFilename].mtime.getTime()) {
fulfill({ contents: self.contents[fullFilename].data, filename: fullFilename});
}
else {
modified = true;
if (!explicit && paths[i] === '.') {
try {
fullFilename = require.resolve(fullFilename);
isNodeModule = true;
}
catch (e) {
filenamesTried.push(npmPrefix + fullFilename);
}
}
}
catch (e) {
modified = true;
}
}
if (modified || !self.contents[fullFilename]) {
const readFileArgs = [fullFilename];
if (!options.rawBuffer) {
readFileArgs.push('utf-8');
}
if (options.syncImport) {
try {
const data = fs.readFileSync.apply(this, readFileArgs);
var stat = fs.statSync.apply(this, [fullFilename]);
self.contents[fullFilename] = { data, mtime: stat.mtime };
fulfill({ contents: data, filename: fullFilename});

let modified = false;

if (self.contents[fullFilename]) {
try {
var stat = fs.statSync.apply(this, [fullFilename]);
if (stat.mtime.getTime() === self.contents[fullFilename].mtime.getTime()) {
fulfill({ contents: self.contents[fullFilename].data, filename: fullFilename});
}
else {
modified = true;
}
}
catch (e) {
modified = true;
}
}
catch (e) {
filenamesTried.push(isNodeModule ? npmPrefix + fullFilename : fullFilename);
return tryPrefix(j + 1);
if (modified || !self.contents[fullFilename]) {
const readFileArgs = [fullFilename];
if (!options.rawBuffer) {
readFileArgs.push('utf-8');
}
if (options.syncImport) {
try {
const data = fs.readFileSync.apply(this, readFileArgs);
var stat = fs.statSync.apply(this, [fullFilename]);
self.contents[fullFilename] = { data, mtime: stat.mtime };
fulfill({ contents: data, filename: fullFilename});
}
catch (e) {
filenamesTried.push(isNodeModule ? npmPrefix + fullFilename : fullFilename);
return tryExtension(k + 1);
}
}
else {
readFileArgs.push(function(e, data) {
if (e) {
filenamesTried.push(isNodeModule ? npmPrefix + fullFilename : fullFilename);
return tryExtension(k + 1);
}
const stat = fs.statSync.apply(this, [fullFilename]);
self.contents[fullFilename] = { data, mtime: stat.mtime };
fulfill({ contents: data, filename: fullFilename});
});
fs.readFile.apply(this, readFileArgs);
}
}
}
else {
readFileArgs.push(function(e, data) {
if (e) {
filenamesTried.push(isNodeModule ? npmPrefix + fullFilename : fullFilename);
return tryPrefix(j + 1);
}
const stat = fs.statSync.apply(this, [fullFilename]);
self.contents[fullFilename] = { data, mtime: stat.mtime };
fulfill({ contents: data, filename: fullFilename});
});
fs.readFile.apply(this, readFileArgs);
tryPrefix(j + 1)
}

}

})(0);
}
else {
tryPathIndex(i + 1);
Expand Down
11 changes: 10 additions & 1 deletion lib/less/environment/abstract-file-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,17 @@ class AbstractFileManager {
return filename.slice(0, j + 1);
}

isPathWithExtension(path, ext) {
let extPos = path.lastIndexOf(ext);
return extPos !== -1 && extPos === path.length - ext.length
}

tryAppendExtension(path, ext) {
return /(\.[a-z]*$)|([\?;].*)$/.test(path) ? path : path + ext;
if (this.isPathWithExtension(path, ext)) {
return path;
}

return path + ext;
}

tryAppendLessExtension(path) {
Expand Down
3 changes: 3 additions & 0 deletions test/css/import-once.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#import {
color: red;
}
.test-d > h1 {
height: 20px;
}
body {
width: 100%;
}
Expand Down
2 changes: 2 additions & 0 deletions test/less/import-once.less
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
@import "import/import-once-test-c";
@import "import/import-once-test-c";
@import "import/import.once.test.d";
@import "import/import.once.test.d.less";
@import "import/import-once-test-c.less";
@import "import/deeper/import-once-test-a";
@import (multiple) "import/import-test-f.less";
Expand Down
3 changes: 3 additions & 0 deletions test/less/import/import.once.test.d.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.test-d > h1 {
height: 20px;
}

1 comment on commit fd66e44

@Jogai
Copy link

@Jogai Jogai commented on fd66e44 Feb 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3.11 fails on lines like these:

@import (less) '~flatpickr/dist/flatpickr.css';
@import (less) '~flatpickr/dist/themes/airbnb.css';
@import (less) '~photoswipe/dist/photoswipe.css';
@import (less) '~photoswipe/dist/default-skin/default-skin.css';

Please sign in to comment.